drivers/gpu/drm/nouveau/dispnv04/dfp.c

Source file repositories/reference/linux-study-clean/drivers/gpu/drm/nouveau/dispnv04/dfp.c

File Facts

System
Linux kernel
Corpus path
drivers/gpu/drm/nouveau/dispnv04/dfp.c
Extension
.c
Size
24731 bytes
Lines
725
Domain
Driver Families
Bucket
drivers/gpu
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

if (is_fpc_off(*fpc)) {
			/* using saved value is ok, as (is_digital && dpms_on &&
			 * fp_control==OFF) is (at present) *only* true when
			 * fpc's most recent change was by below "off" code
			 */
			*fpc = nv_crtc->dpms_saved_fp_control;
		}

		nv_crtc->fp_users |= 1 << nouveau_encoder(encoder)->dcb->index;
		NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_FP_TG_CONTROL, *fpc);
	} else {
		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
			nv_crtc = nouveau_crtc(crtc);
			fpc = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index].fp_control;

			nv_crtc->fp_users &= ~(1 << nouveau_encoder(encoder)->dcb->index);
			if (!is_fpc_off(*fpc) && !nv_crtc->fp_users) {
				nv_crtc->dpms_saved_fp_control = *fpc;
				/* cut the FP output */
				*fpc &= ~FP_TG_CONTROL_ON;
				*fpc |= FP_TG_CONTROL_OFF;
				NVWriteRAMDAC(dev, nv_crtc->index,
					      NV_PRAMDAC_FP_TG_CONTROL, *fpc);
			}
		}
	}
}

static struct drm_encoder *get_tmds_slave(struct drm_encoder *encoder)
{
	struct drm_device *dev = encoder->dev;
	struct dcb_output *dcb = nouveau_encoder(encoder)->dcb;
	struct drm_encoder *slave;

	if (dcb->type != DCB_OUTPUT_TMDS || dcb->location == DCB_LOC_ON_CHIP)
		return NULL;

	/* Some BIOSes (e.g. the one in a Quadro FX1000) report several
	 * TMDS transmitters at the same I2C address, in the same I2C
	 * bus. This can still work because in that case one of them is
	 * always hard-wired to a reasonable configuration using straps,
	 * and the other one needs to be programmed.
	 *
	 * I don't think there's a way to know which is which, even the
	 * blob programs the one exposed via I2C for *both* heads, so
	 * let's do the same.
	 */
	list_for_each_entry(slave, &dev->mode_config.encoder_list, head) {
		struct dcb_output *slave_dcb = nouveau_encoder(slave)->dcb;

		if (slave_dcb->type == DCB_OUTPUT_TMDS && get_encoder_i2c_funcs(slave) &&
		    slave_dcb->tmdsconf.slave_addr == dcb->tmdsconf.slave_addr)
			return slave;
	}

	return NULL;
}

static bool nv04_dfp_mode_fixup(struct drm_encoder *encoder,
				const struct drm_display_mode *mode,
				struct drm_display_mode *adjusted_mode)
{
	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
	struct nouveau_connector *nv_connector =
		nv04_encoder_get_connector(nv_encoder);

	if (!nv_connector->native_mode ||
	    nv_connector->scaling_mode == DRM_MODE_SCALE_NONE ||
	    mode->hdisplay > nv_connector->native_mode->hdisplay ||
	    mode->vdisplay > nv_connector->native_mode->vdisplay) {
		nv_encoder->mode = *adjusted_mode;

	} else {
		nv_encoder->mode = *nv_connector->native_mode;
		adjusted_mode->clock = nv_connector->native_mode->clock;
	}

	return true;
}

static void nv04_dfp_prepare_sel_clk(struct drm_device *dev,
				     struct nouveau_encoder *nv_encoder, int head)
{
	struct nv04_mode_state *state = &nv04_display(dev)->mode_reg;
	uint32_t bits1618 = nv_encoder->dcb->or & DCB_OUTPUT_A ? 0x10000 : 0x40000;

	if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP)
		return;

	/* SEL_CLK is only used on the primary ramdac

Annotation

Implementation Notes