diff options
Diffstat (limited to 'drivers/media/i2c/adv7511.c')
| -rw-r--r-- | drivers/media/i2c/adv7511.c | 148 | 
1 files changed, 98 insertions, 50 deletions
diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index 7a576097471..f98acf4aafd 100644 --- a/drivers/media/i2c/adv7511.c +++ b/drivers/media/i2c/adv7511.c @@ -119,16 +119,14 @@ static int adv7511_s_clock_freq(struct v4l2_subdev *sd, u32 freq);  static const struct v4l2_dv_timings_cap adv7511_timings_cap = {  	.type = V4L2_DV_BT_656_1120, -	.bt = { -		.max_width = ADV7511_MAX_WIDTH, -		.max_height = ADV7511_MAX_HEIGHT, -		.min_pixelclock = ADV7511_MIN_PIXELCLOCK, -		.max_pixelclock = ADV7511_MAX_PIXELCLOCK, -		.standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | +	/* keep this initialization for compatibility with GCC < 4.4.6 */ +	.reserved = { 0 }, +	V4L2_INIT_BT_TIMINGS(0, ADV7511_MAX_WIDTH, 0, ADV7511_MAX_HEIGHT, +		ADV7511_MIN_PIXELCLOCK, ADV7511_MAX_PIXELCLOCK, +		V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |  			V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, -		.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | -			V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM, -	}, +		V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING | +			V4L2_DV_BT_CAP_CUSTOM)  };  static inline struct adv7511_state *get_adv7511_state(struct v4l2_subdev *sd) @@ -454,6 +452,29 @@ static int adv7511_log_status(struct v4l2_subdev *sd)  			  errors[adv7511_rd(sd, 0xc8) >> 4], state->edid_detect_counter,  			  adv7511_rd(sd, 0x94), adv7511_rd(sd, 0x96));  	v4l2_info(sd, "RGB quantization: %s range\n", adv7511_rd(sd, 0x18) & 0x80 ? "limited" : "full"); +	if (adv7511_rd(sd, 0xaf) & 0x02) { +		/* HDMI only */ +		u8 manual_cts = adv7511_rd(sd, 0x0a) & 0x80; +		u32 N = (adv7511_rd(sd, 0x01) & 0xf) << 16 | +			adv7511_rd(sd, 0x02) << 8 | +			adv7511_rd(sd, 0x03); +		u8 vic_detect = adv7511_rd(sd, 0x3e) >> 2; +		u8 vic_sent = adv7511_rd(sd, 0x3d) & 0x3f; +		u32 CTS; + +		if (manual_cts) +			CTS = (adv7511_rd(sd, 0x07) & 0xf) << 16 | +			      adv7511_rd(sd, 0x08) << 8 | +			      adv7511_rd(sd, 0x09); +		else +			CTS = (adv7511_rd(sd, 0x04) & 0xf) << 16 | +			      adv7511_rd(sd, 0x05) << 8 | +			      adv7511_rd(sd, 0x06); +		v4l2_info(sd, "CTS %s mode: N %d, CTS %d\n", +			  manual_cts ? "manual" : "automatic", N, CTS); +		v4l2_info(sd, "VIC: detected %d, sent %d\n", +			  vic_detect, vic_sent); +	}  	if (state->dv_timings.type == V4L2_DV_BT_656_1120)  		v4l2_print_dv_timings(sd->name, "timings: ",  				&state->dv_timings, false); @@ -576,34 +597,6 @@ static int adv7511_isr(struct v4l2_subdev *sd, u32 status, bool *handled)  	return 0;  } -static int adv7511_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid) -{ -	struct adv7511_state *state = get_adv7511_state(sd); - -	if (edid->pad != 0) -		return -EINVAL; -	if ((edid->blocks == 0) || (edid->blocks > 256)) -		return -EINVAL; -	if (!edid->edid) -		return -EINVAL; -	if (!state->edid.segments) { -		v4l2_dbg(1, debug, sd, "EDID segment 0 not found\n"); -		return -ENODATA; -	} -	if (edid->start_block >= state->edid.segments * 2) -		return -E2BIG; -	if ((edid->blocks + edid->start_block) >= state->edid.segments * 2) -		edid->blocks = state->edid.segments * 2 - edid->start_block; - -	memcpy(edid->edid, &state->edid.data[edid->start_block * 128], -			128 * edid->blocks); -	return 0; -} - -static const struct v4l2_subdev_pad_ops adv7511_pad_ops = { -	.get_edid = adv7511_get_edid, -}; -  static const struct v4l2_subdev_core_ops adv7511_core_ops = {  	.log_status = adv7511_log_status,  #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -679,12 +672,18 @@ static int adv7511_g_dv_timings(struct v4l2_subdev *sd,  static int adv7511_enum_dv_timings(struct v4l2_subdev *sd,  				   struct v4l2_enum_dv_timings *timings)  { +	if (timings->pad != 0) +		return -EINVAL; +  	return v4l2_enum_dv_timings_cap(timings, &adv7511_timings_cap, NULL, NULL);  }  static int adv7511_dv_timings_cap(struct v4l2_subdev *sd,  				  struct v4l2_dv_timings_cap *cap)  { +	if (cap->pad != 0) +		return -EINVAL; +  	*cap = adv7511_timings_cap;  	return 0;  } @@ -693,8 +692,6 @@ static const struct v4l2_subdev_video_ops adv7511_video_ops = {  	.s_stream = adv7511_s_stream,  	.s_dv_timings = adv7511_s_dv_timings,  	.g_dv_timings = adv7511_g_dv_timings, -	.enum_dv_timings = adv7511_enum_dv_timings, -	.dv_timings_cap = adv7511_dv_timings_cap,  };  /* ------------------------------ AUDIO OPS ------------------------------ */ @@ -776,6 +773,36 @@ static const struct v4l2_subdev_audio_ops adv7511_audio_ops = {  	.s_routing = adv7511_s_routing,  }; +/* ---------------------------- PAD OPS ------------------------------------- */ + +static int adv7511_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) +{ +	struct adv7511_state *state = get_adv7511_state(sd); + +	if (edid->pad != 0) +		return -EINVAL; +	if ((edid->blocks == 0) || (edid->blocks > 256)) +		return -EINVAL; +	if (!state->edid.segments) { +		v4l2_dbg(1, debug, sd, "EDID segment 0 not found\n"); +		return -ENODATA; +	} +	if (edid->start_block >= state->edid.segments * 2) +		return -E2BIG; +	if ((edid->blocks + edid->start_block) >= state->edid.segments * 2) +		edid->blocks = state->edid.segments * 2 - edid->start_block; + +	memcpy(edid->edid, &state->edid.data[edid->start_block * 128], +			128 * edid->blocks); +	return 0; +} + +static const struct v4l2_subdev_pad_ops adv7511_pad_ops = { +	.get_edid = adv7511_get_edid, +	.enum_dv_timings = adv7511_enum_dv_timings, +	.dv_timings_cap = adv7511_dv_timings_cap, +}; +  /* --------------------- SUBDEV OPS --------------------------------------- */  static const struct v4l2_subdev_ops adv7511_ops = { @@ -944,26 +971,38 @@ static void adv7511_check_monitor_present_status(struct v4l2_subdev *sd)  static bool edid_block_verify_crc(uint8_t *edid_block)  { -	int i;  	uint8_t sum = 0; +	int i;  	for (i = 0; i < 128; i++) -		sum += *(edid_block + i); -	return (sum == 0); +		sum += edid_block[i]; +	return sum == 0;  } -static bool edid_segment_verify_crc(struct v4l2_subdev *sd, u32 segment) +static bool edid_verify_crc(struct v4l2_subdev *sd, u32 segment)  {  	struct adv7511_state *state = get_adv7511_state(sd);  	u32 blocks = state->edid.blocks;  	uint8_t *data = state->edid.data; -	if (edid_block_verify_crc(&data[segment * 256])) { -		if ((segment + 1) * 2 <= blocks) -			return edid_block_verify_crc(&data[segment * 256 + 128]); +	if (!edid_block_verify_crc(&data[segment * 256])) +		return false; +	if ((segment + 1) * 2 <= blocks) +		return edid_block_verify_crc(&data[segment * 256 + 128]); +	return true; +} + +static bool edid_verify_header(struct v4l2_subdev *sd, u32 segment) +{ +	static const u8 hdmi_header[] = { +		0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 +	}; +	struct adv7511_state *state = get_adv7511_state(sd); +	u8 *data = state->edid.data; + +	if (segment != 0)  		return true; -	} -	return false; +	return !memcmp(data, hdmi_header, sizeof(hdmi_header));  }  static bool adv7511_check_edid_status(struct v4l2_subdev *sd) @@ -992,9 +1031,10 @@ static bool adv7511_check_edid_status(struct v4l2_subdev *sd)  			state->edid.blocks = state->edid.data[0x7e] + 1;  			v4l2_dbg(1, debug, sd, "%s: %d blocks in total\n", __func__, state->edid.blocks);  		} -		if (!edid_segment_verify_crc(sd, segment)) { +		if (!edid_verify_crc(sd, segment) || +		    !edid_verify_header(sd, segment)) {  			/* edid crc error, force reread of edid segment */ -			v4l2_dbg(1, debug, sd, "%s: edid crc error\n", __func__); +			v4l2_err(sd, "%s: edid crc or header error\n", __func__);  			state->have_monitor = false;  			adv7511_s_power(sd, false);  			adv7511_s_power(sd, true); @@ -1040,6 +1080,12 @@ static void adv7511_init_setup(struct v4l2_subdev *sd)  	/* clear all interrupts */  	adv7511_wr(sd, 0x96, 0xff); +	/* +	 * Stop HPD from resetting a lot of registers. +	 * It might leave the chip in a partly un-initialized state, +	 * in particular with regards to hotplug bounces. +	 */ +	adv7511_wr_and_or(sd, 0xd6, 0x3f, 0xc0);  	memset(edid, 0, sizeof(struct adv7511_state_edid));  	state->have_monitor = false;  	adv7511_set_isr(sd, false); @@ -1126,6 +1172,7 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *  	state->i2c_edid = i2c_new_dummy(client->adapter, state->i2c_edid_addr >> 1);  	if (state->i2c_edid == NULL) {  		v4l2_err(sd, "failed to register edid i2c client\n"); +		err = -ENOMEM;  		goto err_entity;  	} @@ -1133,6 +1180,7 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *  	state->work_queue = create_singlethread_workqueue(sd->name);  	if (state->work_queue == NULL) {  		v4l2_err(sd, "could not create workqueue\n"); +		err = -ENOMEM;  		goto err_unreg_cec;  	}  | 
