diff options
Diffstat (limited to 'drivers/media/i2c')
66 files changed, 7458 insertions, 2711 deletions
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index d18be19c96c..441053be7f5 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -196,7 +196,7 @@ config VIDEO_ADV7183  config VIDEO_ADV7604  	tristate "Analog Devices ADV7604 decoder" -	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API +	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER  	---help---  	  Support for the Analog Devices ADV7604 video decoder. @@ -208,7 +208,7 @@ config VIDEO_ADV7604  config VIDEO_ADV7842  	tristate "Analog Devices ADV7842 decoder" -	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API +	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER  	---help---  	  Support for the Analog Devices ADV7842 video decoder. @@ -431,7 +431,7 @@ config VIDEO_ADV7393  config VIDEO_ADV7511  	tristate "Analog Devices ADV7511 encoder" -	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API +	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER  	---help---  	  Support for the Analog Devices ADV7511 video encoder. @@ -555,14 +555,6 @@ config VIDEO_MT9V032  	  This is a Video4Linux2 sensor-level driver for the Micron  	  MT9V032 752x480 CMOS sensor. -config VIDEO_TCM825X -	tristate "TCM825x camera sensor support" -	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_INT_DEVICE -	depends on MEDIA_CAMERA_SUPPORT -	---help--- -	  This is a driver for the Toshiba TCM825x VGA camera sensor. -	  It is used for example in Nokia N800. -  config VIDEO_SR030PC30  	tristate "Siliconfile SR030PC30 sensor support"  	depends on I2C && VIDEO_V4L2 @@ -587,6 +579,14 @@ config VIDEO_S5K6AA  	  This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M  	  camera sensor with an embedded SoC image signal processor. +config VIDEO_S5K6A3 +	tristate "Samsung S5K6A3 sensor support" +	depends on MEDIA_CAMERA_SUPPORT +	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API +	---help--- +	  This is a V4L2 sensor-level driver for Samsung S5K6A3 raw +	  camera sensor. +  config VIDEO_S5K4ECGX          tristate "Samsung S5K4ECGX sensor support"          depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API @@ -594,6 +594,13 @@ config VIDEO_S5K4ECGX            This is a V4L2 sensor-level driver for Samsung S5K4ECGX 5M            camera sensor with an embedded SoC image signal processor. +config VIDEO_S5K5BAF +	tristate "Samsung S5K5BAF sensor support" +	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API +	---help--- +	  This is a V4L2 sensor-level driver for Samsung S5K5BAF 2M +	  camera sensor with an embedded SoC image signal processor. +  source "drivers/media/i2c/smiapp/Kconfig"  config VIDEO_S5C73M3 @@ -621,6 +628,24 @@ config VIDEO_AS3645A  	  This is a driver for the AS3645A and LM3555 flash controllers. It has  	  build in control for flash, torch and indicator LEDs. +config VIDEO_LM3560 +	tristate "LM3560 dual flash driver support" +	depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER +	depends on MEDIA_CAMERA_SUPPORT +	select REGMAP_I2C +	---help--- +	  This is a driver for the lm3560 dual flash controllers. It controls +	  flash, torch LEDs. + +config VIDEO_LM3646 +	tristate "LM3646 dual flash driver support" +	depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER +	depends on MEDIA_CAMERA_SUPPORT +	select REGMAP_I2C +	---help--- +	  This is a driver for the lm3646 dual flash controllers. It controls +	  flash, torch LEDs. +  comment "Video improvement chips"  config VIDEO_UPD64031A @@ -646,7 +671,20 @@ config VIDEO_UPD64083  	  To compile this driver as a module, choose M here: the  	  module will be called upd64083. -comment "Miscelaneous helper chips" +comment "Audio/Video compression chips" + +config VIDEO_SAA6752HS +	tristate "Philips SAA6752HS MPEG-2 Audio/Video Encoder" +	depends on VIDEO_V4L2 && I2C +	select CRC32 +	---help--- +	  Support for the Philips SAA6752HS MPEG-2 video and MPEG-audio/AC-3 +	  audio encoder with multiplexer. + +	  To compile this driver as a module, choose M here: the +	  module will be called saa6752hs. + +comment "Miscellaneous helper chips"  config VIDEO_THS7303  	tristate "THS7303/53 Video Amplifier" diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 9f462df77b4..01ae9328e58 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o  obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o  obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o  obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o +obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o  obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o  obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o  obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o @@ -57,7 +58,6 @@ obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o  obj-$(CONFIG_VIDEO_OV7640) += ov7640.o  obj-$(CONFIG_VIDEO_OV7670) += ov7670.o  obj-$(CONFIG_VIDEO_OV9650) += ov9650.o -obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o  obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o  obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o  obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o @@ -66,10 +66,14 @@ obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o  obj-$(CONFIG_VIDEO_SR030PC30)	+= sr030pc30.o  obj-$(CONFIG_VIDEO_NOON010PC30)	+= noon010pc30.o  obj-$(CONFIG_VIDEO_S5K6AA)	+= s5k6aa.o +obj-$(CONFIG_VIDEO_S5K6A3)	+= s5k6a3.o  obj-$(CONFIG_VIDEO_S5K4ECGX)	+= s5k4ecgx.o +obj-$(CONFIG_VIDEO_S5K5BAF)	+= s5k5baf.o  obj-$(CONFIG_VIDEO_S5C73M3)	+= s5c73m3/  obj-$(CONFIG_VIDEO_ADP1653)	+= adp1653.o  obj-$(CONFIG_VIDEO_AS3645A)	+= as3645a.o +obj-$(CONFIG_VIDEO_LM3560)	+= lm3560.o +obj-$(CONFIG_VIDEO_LM3646)	+= lm3646.o  obj-$(CONFIG_VIDEO_SMIAPP_PLL)	+= smiapp-pll.o  obj-$(CONFIG_VIDEO_AK881X)		+= ak881x.o  obj-$(CONFIG_VIDEO_IR_I2C)  += ir-kbd-i2c.o diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index bb0c99d7a4f..fada1756620 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -66,11 +66,6 @@ MODULE_LICENSE("GPL");  **********************************************************************  */ -struct i2c_reg_value { -	u8 reg; -	u8 value; -}; -  struct ad9389b_state_edid {  	/* total number of blocks */  	u32 blocks; @@ -143,14 +138,14 @@ static int ad9389b_wr(struct v4l2_subdev *sd, u8 reg, u8 val)  		if (ret == 0)  			return 0;  	} -	v4l2_err(sd, "I2C Write Problem\n"); +	v4l2_err(sd, "%s: failed reg 0x%x, val 0x%x\n", __func__, reg, val);  	return ret;  }  /* To set specific bits in the register, a clear-mask is given (to be AND-ed),     and then the value-mask (to be OR-ed). */  static inline void ad9389b_wr_and_or(struct v4l2_subdev *sd, u8 reg, -						u8 clr_mask, u8 val_mask) +				     u8 clr_mask, u8 val_mask)  {  	ad9389b_wr(sd, reg, (ad9389b_rd(sd, reg) & clr_mask) | val_mask);  } @@ -321,12 +316,12 @@ static int ad9389b_s_ctrl(struct v4l2_ctrl *ctrl)  	struct ad9389b_state *state = get_ad9389b_state(sd);  	v4l2_dbg(1, debug, sd, -		"%s: ctrl id: %d, ctrl->val %d\n", __func__, ctrl->id, ctrl->val); +		 "%s: ctrl id: %d, ctrl->val %d\n", __func__, ctrl->id, ctrl->val);  	if (state->hdmi_mode_ctrl == ctrl) {  		/* Set HDMI or DVI-D */  		ad9389b_wr_and_or(sd, 0xaf, 0xfd, -				ctrl->val == V4L2_DV_TX_MODE_HDMI ? 0x02 : 0x00); +				  ctrl->val == V4L2_DV_TX_MODE_HDMI ? 0x02 : 0x00);  		return 0;  	}  	if (state->rgb_quantization_range_ctrl == ctrl) @@ -387,61 +382,57 @@ static int ad9389b_log_status(struct v4l2_subdev *sd)  	v4l2_info(sd, "chip revision %d\n", state->chip_revision);  	v4l2_info(sd, "power %s\n", state->power_on ? "on" : "off");  	v4l2_info(sd, "%s hotplug, %s Rx Sense, %s EDID (%d block(s))\n", -			(ad9389b_rd(sd, 0x42) & MASK_AD9389B_HPD_DETECT) ? -							"detected" : "no", -			(ad9389b_rd(sd, 0x42) & MASK_AD9389B_MSEN_DETECT) ? -							"detected" : "no", -			edid->segments ? "found" : "no", edid->blocks); -	if (state->have_monitor) { -		v4l2_info(sd, "%s output %s\n", -				  (ad9389b_rd(sd, 0xaf) & 0x02) ? -				  "HDMI" : "DVI-D", -				  (ad9389b_rd(sd, 0xa1) & 0x3c) ? -				  "disabled" : "enabled"); -	} +		  (ad9389b_rd(sd, 0x42) & MASK_AD9389B_HPD_DETECT) ? +		  "detected" : "no", +		  (ad9389b_rd(sd, 0x42) & MASK_AD9389B_MSEN_DETECT) ? +		  "detected" : "no", +		  edid->segments ? "found" : "no", edid->blocks); +	v4l2_info(sd, "%s output %s\n", +		  (ad9389b_rd(sd, 0xaf) & 0x02) ? +		  "HDMI" : "DVI-D", +		  (ad9389b_rd(sd, 0xa1) & 0x3c) ? +		  "disabled" : "enabled");  	v4l2_info(sd, "ad9389b: %s\n", (ad9389b_rd(sd, 0xb8) & 0x40) ? -					"encrypted" : "no encryption"); +		  "encrypted" : "no encryption");  	v4l2_info(sd, "state: %s, error: %s, detect count: %u, msk/irq: %02x/%02x\n", -			states[ad9389b_rd(sd, 0xc8) & 0xf], -			errors[ad9389b_rd(sd, 0xc8) >> 4], -			state->edid_detect_counter, -			ad9389b_rd(sd, 0x94), ad9389b_rd(sd, 0x96)); +		  states[ad9389b_rd(sd, 0xc8) & 0xf], +		  errors[ad9389b_rd(sd, 0xc8) >> 4], +		  state->edid_detect_counter, +		  ad9389b_rd(sd, 0x94), ad9389b_rd(sd, 0x96));  	manual_gear = ad9389b_rd(sd, 0x98) & 0x80;  	v4l2_info(sd, "ad9389b: RGB quantization: %s range\n", -			ad9389b_rd(sd, 0x3b) & 0x01 ? "limited" : "full"); +		  ad9389b_rd(sd, 0x3b) & 0x01 ? "limited" : "full");  	v4l2_info(sd, "ad9389b: %s gear %d\n",  		  manual_gear ? "manual" : "automatic",  		  manual_gear ? ((ad9389b_rd(sd, 0x98) & 0x70) >> 4) : -				((ad9389b_rd(sd, 0x9e) & 0x0e) >> 1)); -	if (state->have_monitor) { -		if (ad9389b_rd(sd, 0xaf) & 0x02) { -			/* HDMI only */ -			u8 manual_cts = ad9389b_rd(sd, 0x0a) & 0x80; -			u32 N = (ad9389b_rd(sd, 0x01) & 0xf) << 16 | -				 ad9389b_rd(sd, 0x02) << 8 | -				 ad9389b_rd(sd, 0x03); -			u8 vic_detect = ad9389b_rd(sd, 0x3e) >> 2; -			u8 vic_sent = ad9389b_rd(sd, 0x3d) & 0x3f; -			u32 CTS; - -			if (manual_cts) -				CTS = (ad9389b_rd(sd, 0x07) & 0xf) << 16 | -				       ad9389b_rd(sd, 0x08) << 8 | -				       ad9389b_rd(sd, 0x09); -			else -				CTS = (ad9389b_rd(sd, 0x04) & 0xf) << 16 | -				       ad9389b_rd(sd, 0x05) << 8 | -				       ad9389b_rd(sd, 0x06); -			N = (ad9389b_rd(sd, 0x01) & 0xf) << 16 | -			     ad9389b_rd(sd, 0x02) << 8 | -			     ad9389b_rd(sd, 0x03); - -			v4l2_info(sd, "ad9389b: CTS %s mode: N %d, CTS %d\n", -				manual_cts ? "manual" : "automatic", N, CTS); - -			v4l2_info(sd, "ad9389b: VIC: detected %d, sent %d\n", -				vic_detect, vic_sent); -		} +		  ((ad9389b_rd(sd, 0x9e) & 0x0e) >> 1)); +	if (ad9389b_rd(sd, 0xaf) & 0x02) { +		/* HDMI only */ +		u8 manual_cts = ad9389b_rd(sd, 0x0a) & 0x80; +		u32 N = (ad9389b_rd(sd, 0x01) & 0xf) << 16 | +			ad9389b_rd(sd, 0x02) << 8 | +			ad9389b_rd(sd, 0x03); +		u8 vic_detect = ad9389b_rd(sd, 0x3e) >> 2; +		u8 vic_sent = ad9389b_rd(sd, 0x3d) & 0x3f; +		u32 CTS; + +		if (manual_cts) +			CTS = (ad9389b_rd(sd, 0x07) & 0xf) << 16 | +			      ad9389b_rd(sd, 0x08) << 8 | +			      ad9389b_rd(sd, 0x09); +		else +			CTS = (ad9389b_rd(sd, 0x04) & 0xf) << 16 | +			      ad9389b_rd(sd, 0x05) << 8 | +			      ad9389b_rd(sd, 0x06); +		N = (ad9389b_rd(sd, 0x01) & 0xf) << 16 | +		    ad9389b_rd(sd, 0x02) << 8 | +		    ad9389b_rd(sd, 0x03); + +		v4l2_info(sd, "ad9389b: CTS %s mode: N %d, CTS %d\n", +			  manual_cts ? "manual" : "automatic", N, CTS); + +		v4l2_info(sd, "ad9389b: 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: ", @@ -486,7 +477,7 @@ static int ad9389b_s_power(struct v4l2_subdev *sd, int on)  	}  	if (i > 1)  		v4l2_dbg(1, debug, sd, -			"needed %d retries to powerup the ad9389b\n", i); +			 "needed %d retries to powerup the ad9389b\n", i);  	/* Select chip: AD9389B */  	ad9389b_wr_and_or(sd, 0xba, 0xef, 0x10); @@ -556,14 +547,16 @@ static int ad9389b_isr(struct v4l2_subdev *sd, u32 status, bool *handled)  	irq_status = ad9389b_rd(sd, 0x96);  	/* clear detected interrupts */  	ad9389b_wr(sd, 0x96, irq_status); +	/* enable interrupts */ +	ad9389b_set_isr(sd, true); + +	v4l2_dbg(1, debug, sd, "%s: irq_status 0x%x\n", __func__, irq_status); -	if (irq_status & (MASK_AD9389B_HPD_INT | MASK_AD9389B_MSEN_INT)) +	if (irq_status & (MASK_AD9389B_HPD_INT))  		ad9389b_check_monitor_present_status(sd);  	if (irq_status & MASK_AD9389B_EDID_RDY_INT)  		ad9389b_check_edid_status(sd); -	/* enable interrupts */ -	ad9389b_set_isr(sd, true);  	*handled = true;  	return 0;  } @@ -578,42 +571,11 @@ static const struct v4l2_subdev_core_ops ad9389b_core_ops = {  	.interrupt_service_routine = ad9389b_isr,  }; -/* ------------------------------ PAD OPS ------------------------------ */ - -static int ad9389b_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid) -{ -	struct ad9389b_state *state = get_ad9389b_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 ad9389b_pad_ops = { -	.get_edid = ad9389b_get_edid, -}; -  /* ------------------------------ VIDEO OPS ------------------------------ */  /* Enable/disable ad9389b output */  static int ad9389b_s_stream(struct v4l2_subdev *sd, int enable)  { -	struct ad9389b_state *state = get_ad9389b_state(sd); -  	v4l2_dbg(1, debug, sd, "%s: %sable\n", __func__, (enable ? "en" : "dis"));  	ad9389b_wr_and_or(sd, 0xa1, ~0x3c, (enable ? 0 : 0x3c)); @@ -621,23 +583,19 @@ static int ad9389b_s_stream(struct v4l2_subdev *sd, int enable)  		ad9389b_check_monitor_present_status(sd);  	} else {  		ad9389b_s_power(sd, 0); -		state->have_monitor = false;  	}  	return 0;  }  static const struct v4l2_dv_timings_cap ad9389b_timings_cap = {  	.type = V4L2_DV_BT_656_1120, -	.bt = { -		.max_width = 1920, -		.max_height = 1200, -		.min_pixelclock = 25000000, -		.max_pixelclock = 170000000, -		.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, 1920, 0, 1200, 25000000, 170000000, +		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 int ad9389b_s_dv_timings(struct v4l2_subdev *sd, @@ -689,15 +647,21 @@ static int ad9389b_g_dv_timings(struct v4l2_subdev *sd,  }  static int ad9389b_enum_dv_timings(struct v4l2_subdev *sd, -			struct v4l2_enum_dv_timings *timings) +				   struct v4l2_enum_dv_timings *timings)  { +	if (timings->pad != 0) +		return -EINVAL; +  	return v4l2_enum_dv_timings_cap(timings, &ad9389b_timings_cap,  			NULL, NULL);  }  static int ad9389b_dv_timings_cap(struct v4l2_subdev *sd, -			struct v4l2_dv_timings_cap *cap) +				  struct v4l2_dv_timings_cap *cap)  { +	if (cap->pad != 0) +		return -EINVAL; +  	*cap = ad9389b_timings_cap;  	return 0;  } @@ -706,10 +670,39 @@ static const struct v4l2_subdev_video_ops ad9389b_video_ops = {  	.s_stream = ad9389b_s_stream,  	.s_dv_timings = ad9389b_s_dv_timings,  	.g_dv_timings = ad9389b_g_dv_timings, +}; + +/* ------------------------------ PAD OPS ------------------------------ */ + +static int ad9389b_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) +{ +	struct ad9389b_state *state = get_ad9389b_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 ad9389b_pad_ops = { +	.get_edid = ad9389b_get_edid,  	.enum_dv_timings = ad9389b_enum_dv_timings,  	.dv_timings_cap = ad9389b_dv_timings_cap,  }; +/* ------------------------------ AUDIO OPS ------------------------------ */ +  static int ad9389b_s_audio_stream(struct v4l2_subdev *sd, int enable)  {  	v4l2_dbg(1, debug, sd, "%s: %sable\n", __func__, (enable ? "en" : "dis")); @@ -727,15 +720,15 @@ static int ad9389b_s_clock_freq(struct v4l2_subdev *sd, u32 freq)  	u32 N;  	switch (freq) { -	case 32000: N = 4096; break; -	case 44100: N = 6272; break; -	case 48000: N = 6144; break; -	case 88200: N = 12544; break; -	case 96000: N = 12288; break; +	case 32000:  N = 4096;  break; +	case 44100:  N = 6272;  break; +	case 48000:  N = 6144;  break; +	case 88200:  N = 12544; break; +	case 96000:  N = 12288; break;  	case 176400: N = 25088; break;  	case 192000: N = 24576; break;  	default: -		return -EINVAL; +	     return -EINVAL;  	}  	/* Set N (used with CTS to regenerate the audio clock) */ @@ -751,15 +744,15 @@ static int ad9389b_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq)  	u32 i2s_sf;  	switch (freq) { -	case 32000: i2s_sf = 0x30; break; -	case 44100: i2s_sf = 0x00; break; -	case 48000: i2s_sf = 0x20; break; -	case 88200: i2s_sf = 0x80; break; -	case 96000: i2s_sf = 0xa0; break; +	case 32000:  i2s_sf = 0x30; break; +	case 44100:  i2s_sf = 0x00; break; +	case 48000:  i2s_sf = 0x20; break; +	case 88200:  i2s_sf = 0x80; break; +	case 96000:  i2s_sf = 0xa0; break;  	case 176400: i2s_sf = 0xc0; break;  	case 192000: i2s_sf = 0xe0; break;  	default: -		return -EINVAL; +	     return -EINVAL;  	}  	/* Set sampling frequency for I2S audio to 48 kHz */ @@ -803,7 +796,7 @@ static const struct v4l2_subdev_ops ad9389b_ops = {  /* ----------------------------------------------------------------------- */  static void ad9389b_dbg_dump_edid(int lvl, int debug, struct v4l2_subdev *sd, -							int segment, u8 *buf) +				  int segment, u8 *buf)  {  	int i, j; @@ -829,8 +822,8 @@ static void ad9389b_dbg_dump_edid(int lvl, int debug, struct v4l2_subdev *sd,  static void ad9389b_edid_handler(struct work_struct *work)  {  	struct delayed_work *dwork = to_delayed_work(work); -	struct ad9389b_state *state = container_of(dwork, -			struct ad9389b_state, edid_handler); +	struct ad9389b_state *state = +		container_of(dwork, struct ad9389b_state, edid_handler);  	struct v4l2_subdev *sd = &state->sd;  	struct ad9389b_edid_detect ed; @@ -848,11 +841,10 @@ static void ad9389b_edid_handler(struct work_struct *work)  		if (state->edid.read_retries) {  			state->edid.read_retries--;  			v4l2_dbg(1, debug, sd, "%s: edid read failed\n", __func__); -			state->have_monitor = false;  			ad9389b_s_power(sd, false);  			ad9389b_s_power(sd, true);  			queue_delayed_work(state->work_queue, -					&state->edid_handler, EDID_DELAY); +					   &state->edid_handler, EDID_DELAY);  			return;  		}  	} @@ -918,49 +910,35 @@ static void ad9389b_notify_monitor_detect(struct v4l2_subdev *sd)  	v4l2_subdev_notify(sd, AD9389B_MONITOR_DETECT, (void *)&mdt);  } -static void ad9389b_check_monitor_present_status(struct v4l2_subdev *sd) +static void ad9389b_update_monitor_present_status(struct v4l2_subdev *sd)  {  	struct ad9389b_state *state = get_ad9389b_state(sd);  	/* read hotplug and rx-sense state */  	u8 status = ad9389b_rd(sd, 0x42);  	v4l2_dbg(1, debug, sd, "%s: status: 0x%x%s%s\n", -			 __func__, -			 status, -			 status & MASK_AD9389B_HPD_DETECT ? ", hotplug" : "", -			 status & MASK_AD9389B_MSEN_DETECT ? ", rx-sense" : ""); +		 __func__, +		 status, +		 status & MASK_AD9389B_HPD_DETECT ? ", hotplug" : "", +		 status & MASK_AD9389B_MSEN_DETECT ? ", rx-sense" : ""); -	if ((status & MASK_AD9389B_HPD_DETECT) && -	    ((status & MASK_AD9389B_MSEN_DETECT) || state->edid.segments)) { -		v4l2_dbg(1, debug, sd, -				"%s: hotplug and (rx-sense or edid)\n", __func__); -		if (!state->have_monitor) { -			v4l2_dbg(1, debug, sd, "%s: monitor detected\n", __func__); -			state->have_monitor = true; -			ad9389b_set_isr(sd, true); -			if (!ad9389b_s_power(sd, true)) { -				v4l2_dbg(1, debug, sd, -					"%s: monitor detected, powerup failed\n", __func__); -				return; -			} -			ad9389b_setup(sd); -			ad9389b_notify_monitor_detect(sd); -			state->edid.read_retries = EDID_MAX_RETRIES; -			queue_delayed_work(state->work_queue, -					&state->edid_handler, EDID_DELAY); -		} -	} else if (status & MASK_AD9389B_HPD_DETECT) { +	if (status & MASK_AD9389B_HPD_DETECT) {  		v4l2_dbg(1, debug, sd, "%s: hotplug detected\n", __func__); +		state->have_monitor = true; +		if (!ad9389b_s_power(sd, true)) { +			v4l2_dbg(1, debug, sd, +				 "%s: monitor detected, powerup failed\n", __func__); +			return; +		} +		ad9389b_setup(sd); +		ad9389b_notify_monitor_detect(sd);  		state->edid.read_retries = EDID_MAX_RETRIES;  		queue_delayed_work(state->work_queue, -				&state->edid_handler, EDID_DELAY); +				   &state->edid_handler, EDID_DELAY);  	} else if (!(status & MASK_AD9389B_HPD_DETECT)) {  		v4l2_dbg(1, debug, sd, "%s: hotplug not detected\n", __func__); -		if (state->have_monitor) { -			v4l2_dbg(1, debug, sd, "%s: monitor not detected\n", __func__); -			state->have_monitor = false; -			ad9389b_notify_monitor_detect(sd); -		} +		state->have_monitor = false; +		ad9389b_notify_monitor_detect(sd);  		ad9389b_s_power(sd, false);  		memset(&state->edid, 0, sizeof(struct ad9389b_state_edid));  	} @@ -969,6 +947,35 @@ static void ad9389b_check_monitor_present_status(struct v4l2_subdev *sd)  	v4l2_ctrl_s_ctrl(state->hotplug_ctrl, ad9389b_have_hotplug(sd) ? 0x1 : 0x0);  	v4l2_ctrl_s_ctrl(state->rx_sense_ctrl, ad9389b_have_rx_sense(sd) ? 0x1 : 0x0);  	v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, state->edid.segments ? 0x1 : 0x0); + +	/* update with setting from ctrls */ +	ad9389b_s_ctrl(state->rgb_quantization_range_ctrl); +	ad9389b_s_ctrl(state->hdmi_mode_ctrl); +} + +static void ad9389b_check_monitor_present_status(struct v4l2_subdev *sd) +{ +	struct ad9389b_state *state = get_ad9389b_state(sd); +	int retry = 0; + +	ad9389b_update_monitor_present_status(sd); + +	/* +	 * Rapid toggling of the hotplug may leave the chip powered off, +	 * even if we think it is on. In that case reset and power up again. +	 */ +	while (state->power_on && (ad9389b_rd(sd, 0x41) & 0x40)) { +		if (++retry > 5) { +			v4l2_err(sd, "retried %d times, give up\n", retry); +			return; +		} +		v4l2_dbg(1, debug, sd, "%s: reset and re-check status (%d)\n", __func__, retry); +		ad9389b_notify_monitor_detect(sd); +		cancel_delayed_work_sync(&state->edid_handler); +		memset(&state->edid, 0, sizeof(struct ad9389b_state_edid)); +		ad9389b_s_power(sd, false); +		ad9389b_update_monitor_present_status(sd); +	}  }  static bool edid_block_verify_crc(u8 *edid_block) @@ -981,7 +988,7 @@ static bool edid_block_verify_crc(u8 *edid_block)  	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 ad9389b_state *state = get_ad9389b_state(sd);  	u32 blocks = state->edid.blocks; @@ -995,6 +1002,25 @@ static bool edid_segment_verify_crc(struct v4l2_subdev *sd, u32 segment)  	return false;  } +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 ad9389b_state *state = get_ad9389b_state(sd); +	u8 *data = state->edid.data; +	int i; + +	if (segment) +		return true; + +	for (i = 0; i < ARRAY_SIZE(hdmi_header); i++) +		if (data[i] != hdmi_header[i]) +			return false; + +	return true; +} +  static bool ad9389b_check_edid_status(struct v4l2_subdev *sd)  {  	struct ad9389b_state *state = get_ad9389b_state(sd); @@ -1003,7 +1029,7 @@ static bool ad9389b_check_edid_status(struct v4l2_subdev *sd)  	u8 edidRdy = ad9389b_rd(sd, 0xc5);  	v4l2_dbg(1, debug, sd, "%s: edid ready (retries: %d)\n", -			 __func__, EDID_MAX_RETRIES - state->edid.read_retries); +		 __func__, EDID_MAX_RETRIES - state->edid.read_retries);  	if (!(edidRdy & MASK_AD9389B_EDID_RDY))  		return false; @@ -1016,16 +1042,16 @@ static bool ad9389b_check_edid_status(struct v4l2_subdev *sd)  	v4l2_dbg(1, debug, sd, "%s: got segment %d\n", __func__, segment);  	ad9389b_edid_rd(sd, 256, &state->edid.data[segment * 256]);  	ad9389b_dbg_dump_edid(2, debug, sd, segment, -			&state->edid.data[segment * 256]); +			      &state->edid.data[segment * 256]);  	if (segment == 0) {  		state->edid.blocks = state->edid.data[0x7e] + 1;  		v4l2_dbg(1, debug, sd, "%s: %d blocks in total\n", -				__func__, state->edid.blocks); +			 __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_err(sd, "%s: edid crc error\n", __func__); -		state->have_monitor = false; +		v4l2_err(sd, "%s: edid crc or header error\n", __func__);  		ad9389b_s_power(sd, false);  		ad9389b_s_power(sd, true);  		return false; @@ -1035,12 +1061,12 @@ static bool ad9389b_check_edid_status(struct v4l2_subdev *sd)  	if (((state->edid.data[0x7e] >> 1) + 1) > state->edid.segments) {  		/* Request next EDID segment */  		v4l2_dbg(1, debug, sd, "%s: request segment %d\n", -				__func__, state->edid.segments); +			 __func__, state->edid.segments);  		ad9389b_wr(sd, 0xc9, 0xf);  		ad9389b_wr(sd, 0xc4, state->edid.segments);  		state->edid.read_retries = EDID_MAX_RETRIES;  		queue_delayed_work(state->work_queue, -				&state->edid_handler, EDID_DELAY); +				   &state->edid_handler, EDID_DELAY);  		return false;  	} @@ -1084,7 +1110,7 @@ static int ad9389b_probe(struct i2c_client *client, const struct i2c_device_id *  		return -EIO;  	v4l_dbg(1, debug, client, "detecting ad9389b client on address 0x%x\n", -			client->addr << 1); +		client->addr << 1);  	state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);  	if (!state) @@ -1143,7 +1169,7 @@ static int ad9389b_probe(struct i2c_client *client, const struct i2c_device_id *  		goto err_entity;  	}  	v4l2_dbg(1, debug, sd, "reg 0x41 0x%x, chip version (reg 0x00) 0x%x\n", -			ad9389b_rd(sd, 0x41), state->chip_revision); +		 ad9389b_rd(sd, 0x41), state->chip_revision);  	state->edid_i2c_client = i2c_new_dummy(client->adapter, (0x7e>>1));  	if (state->edid_i2c_client == NULL) { @@ -1166,7 +1192,7 @@ static int ad9389b_probe(struct i2c_client *client, const struct i2c_device_id *  	ad9389b_set_isr(sd, true);  	v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, -			  client->addr << 1, client->adapter->name); +		  client->addr << 1, client->adapter->name);  	return 0;  err_unreg: diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index d7d99f1c69e..ac1cdbe251a 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -123,11 +123,11 @@  struct adv7180_state {  	struct v4l2_ctrl_handler ctrl_hdl;  	struct v4l2_subdev	sd; -	struct work_struct	work;  	struct mutex		mutex; /* mutual excl. when accessing chip */  	int			irq;  	v4l2_std_id		curr_norm;  	bool			autodetect; +	bool			powered;  	u8			input;  };  #define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler,		\ @@ -312,6 +312,37 @@ out:  	return ret;  } +static int adv7180_set_power(struct adv7180_state *state, +	struct i2c_client *client, bool on) +{ +	u8 val; + +	if (on) +		val = ADV7180_PWR_MAN_ON; +	else +		val = ADV7180_PWR_MAN_OFF; + +	return i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, val); +} + +static int adv7180_s_power(struct v4l2_subdev *sd, int on) +{ +	struct adv7180_state *state = to_state(sd); +	struct i2c_client *client = v4l2_get_subdevdata(sd); +	int ret; + +	ret = mutex_lock_interruptible(&state->mutex); +	if (ret) +		return ret; + +	ret = adv7180_set_power(state, client, on); +	if (ret == 0) +		state->powered = on; + +	mutex_unlock(&state->mutex); +	return ret; +} +  static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl)  {  	struct v4l2_subdev *sd = to_adv7180_sd(ctrl); @@ -430,6 +461,7 @@ static int adv7180_g_mbus_config(struct v4l2_subdev *sd,  }  static const struct v4l2_subdev_video_ops adv7180_video_ops = { +	.s_std = adv7180_s_std,  	.querystd = adv7180_querystd,  	.g_input_status = adv7180_g_input_status,  	.s_routing = adv7180_s_routing, @@ -441,7 +473,7 @@ static const struct v4l2_subdev_video_ops adv7180_video_ops = {  };  static const struct v4l2_subdev_core_ops adv7180_core_ops = { -	.s_std = adv7180_s_std, +	.s_power = adv7180_s_power,  };  static const struct v4l2_subdev_ops adv7180_ops = { @@ -449,10 +481,9 @@ static const struct v4l2_subdev_ops adv7180_ops = {  	.video = &adv7180_video_ops,  }; -static void adv7180_work(struct work_struct *work) +static irqreturn_t adv7180_irq(int irq, void *devid)  { -	struct adv7180_state *state = container_of(work, struct adv7180_state, -						   work); +	struct adv7180_state *state = devid;  	struct i2c_client *client = v4l2_get_subdevdata(&state->sd);  	u8 isr3; @@ -468,17 +499,6 @@ static void adv7180_work(struct work_struct *work)  		__adv7180_status(client, NULL, &state->curr_norm);  	mutex_unlock(&state->mutex); -	enable_irq(state->irq); -} - -static irqreturn_t adv7180_irq(int irq, void *devid) -{ -	struct adv7180_state *state = devid; - -	schedule_work(&state->work); - -	disable_irq_nosync(state->irq); -  	return IRQ_HANDLED;  } @@ -533,48 +553,52 @@ static int init_device(struct i2c_client *client, struct adv7180_state *state)  	/* register for interrupts */  	if (state->irq > 0) { -		ret = request_irq(state->irq, adv7180_irq, 0, KBUILD_MODNAME, -				  state); +		ret = request_threaded_irq(state->irq, NULL, adv7180_irq, +					   IRQF_ONESHOT, KBUILD_MODNAME, state);  		if (ret)  			return ret;  		ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,  						ADV7180_ADI_CTRL_IRQ_SPACE);  		if (ret < 0) -			return ret; +			goto err;  		/* config the Interrupt pin to be active low */  		ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI,  						ADV7180_ICONF1_ACTIVE_LOW |  						ADV7180_ICONF1_PSYNC_ONLY);  		if (ret < 0) -			return ret; +			goto err;  		ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0);  		if (ret < 0) -			return ret; +			goto err;  		ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0);  		if (ret < 0) -			return ret; +			goto err;  		/* enable AD change interrupts interrupts */  		ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI,  						ADV7180_IRQ3_AD_CHANGE);  		if (ret < 0) -			return ret; +			goto err;  		ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0);  		if (ret < 0) -			return ret; +			goto err;  		ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,  						0);  		if (ret < 0) -			return ret; +			goto err;  	}  	return 0; + +err: +	free_irq(state->irq, state); +	return ret;  }  static int adv7180_probe(struct i2c_client *client, @@ -598,9 +622,9 @@ static int adv7180_probe(struct i2c_client *client,  	}  	state->irq = client->irq; -	INIT_WORK(&state->work, adv7180_work);  	mutex_init(&state->mutex);  	state->autodetect = true; +	state->powered = true;  	state->input = 0;  	sd = &state->sd;  	v4l2_i2c_subdev_init(sd, client, &adv7180_ops); @@ -611,15 +635,21 @@ static int adv7180_probe(struct i2c_client *client,  	ret = init_device(client, state);  	if (ret)  		goto err_free_ctrl; + +	ret = v4l2_async_register_subdev(sd); +	if (ret) +		goto err_free_irq; +  	return 0; +err_free_irq: +	if (state->irq > 0) +		free_irq(client->irq, state);  err_free_ctrl:  	adv7180_exit_controls(state);  err_unreg_subdev:  	mutex_destroy(&state->mutex); -	v4l2_device_unregister_subdev(sd);  err: -	printk(KERN_ERR KBUILD_MODNAME ": Failed to probe: %d\n", ret);  	return ret;  } @@ -628,20 +658,14 @@ static int adv7180_remove(struct i2c_client *client)  	struct v4l2_subdev *sd = i2c_get_clientdata(client);  	struct adv7180_state *state = to_state(sd); -	if (state->irq > 0) { +	v4l2_async_unregister_subdev(sd); + +	if (state->irq > 0)  		free_irq(client->irq, state); -		if (cancel_work_sync(&state->work)) { -			/* -			 * Work was pending, therefore we need to enable -			 * IRQ here to balance the disable_irq() done in the -			 * interrupt handler. -			 */ -			enable_irq(state->irq); -		} -	} -	mutex_destroy(&state->mutex);  	v4l2_device_unregister_subdev(sd); +	adv7180_exit_controls(state); +	mutex_destroy(&state->mutex);  	return 0;  } @@ -654,13 +678,10 @@ static const struct i2c_device_id adv7180_id[] = {  static int adv7180_suspend(struct device *dev)  {  	struct i2c_client *client = to_i2c_client(dev); -	int ret; +	struct v4l2_subdev *sd = i2c_get_clientdata(client); +	struct adv7180_state *state = to_state(sd); -	ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, -					ADV7180_PWR_MAN_OFF); -	if (ret < 0) -		return ret; -	return 0; +	return adv7180_set_power(state, client, false);  }  static int adv7180_resume(struct device *dev) @@ -670,10 +691,11 @@ static int adv7180_resume(struct device *dev)  	struct adv7180_state *state = to_state(sd);  	int ret; -	ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, -					ADV7180_PWR_MAN_ON); -	if (ret < 0) -		return ret; +	if (state->powered) { +		ret = adv7180_set_power(state, client, true); +		if (ret) +			return ret; +	}  	ret = init_device(client, state);  	if (ret < 0)  		return ret; diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c index 6f738d8e3a8..df461b07b2f 100644 --- a/drivers/media/i2c/adv7183.c +++ b/drivers/media/i2c/adv7183.c @@ -178,7 +178,7 @@ static int adv7183_log_status(struct v4l2_subdev *sd)  			adv7183_read(sd, ADV7183_VS_FIELD_CTRL_1),  			adv7183_read(sd, ADV7183_VS_FIELD_CTRL_2),  			adv7183_read(sd, ADV7183_VS_FIELD_CTRL_3)); -	v4l2_info(sd, "adv7183: Hsync positon control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n", +	v4l2_info(sd, "adv7183: Hsync position control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",  			adv7183_read(sd, ADV7183_HS_POS_CTRL_1),  			adv7183_read(sd, ADV7183_HS_POS_CTRL_2),  			adv7183_read(sd, ADV7183_HS_POS_CTRL_3)); @@ -501,8 +501,6 @@ static const struct v4l2_ctrl_ops adv7183_ctrl_ops = {  static const struct v4l2_subdev_core_ops adv7183_core_ops = {  	.log_status = adv7183_log_status, -	.g_std = adv7183_g_std, -	.s_std = adv7183_s_std,  	.reset = adv7183_reset,  #ifdef CONFIG_VIDEO_ADV_DEBUG  	.g_register = adv7183_g_register, @@ -511,6 +509,8 @@ static const struct v4l2_subdev_core_ops adv7183_core_ops = {  };  static const struct v4l2_subdev_video_ops adv7183_video_ops = { +	.g_std = adv7183_g_std, +	.s_std = adv7183_s_std,  	.s_routing = adv7183_s_routing,  	.querystd = adv7183_querystd,  	.g_input_status = adv7183_g_input_status, diff --git a/drivers/media/i2c/adv7183_regs.h b/drivers/media/i2c/adv7183_regs.h index 4a5b7d211d2..b253d400e81 100644 --- a/drivers/media/i2c/adv7183_regs.h +++ b/drivers/media/i2c/adv7183_regs.h @@ -52,9 +52,9 @@  #define ADV7183_VS_FIELD_CTRL_1    0x31 /* Vsync field control 1 */  #define ADV7183_VS_FIELD_CTRL_2    0x32 /* Vsync field control 2 */  #define ADV7183_VS_FIELD_CTRL_3    0x33 /* Vsync field control 3 */ -#define ADV7183_HS_POS_CTRL_1      0x34 /* Hsync positon control 1 */ -#define ADV7183_HS_POS_CTRL_2      0x35 /* Hsync positon control 2 */ -#define ADV7183_HS_POS_CTRL_3      0x36 /* Hsync positon control 3 */ +#define ADV7183_HS_POS_CTRL_1      0x34 /* Hsync position control 1 */ +#define ADV7183_HS_POS_CTRL_2      0x35 /* Hsync position control 2 */ +#define ADV7183_HS_POS_CTRL_3      0x36 /* Hsync position control 3 */  #define ADV7183_POLARITY           0x37 /* Polarity */  #define ADV7183_NTSC_COMB_CTRL     0x38 /* NTSC comb control */  #define ADV7183_PAL_COMB_CTRL      0x39 /* PAL comb control */ diff --git a/drivers/media/i2c/adv7343.c b/drivers/media/i2c/adv7343.c index aeb56c53e39..9d38f7b36cd 100644 --- a/drivers/media/i2c/adv7343.c +++ b/drivers/media/i2c/adv7343.c @@ -25,12 +25,13 @@  #include <linux/module.h>  #include <linux/videodev2.h>  #include <linux/uaccess.h> +#include <linux/of.h> +#include <linux/of_graph.h>  #include <media/adv7343.h>  #include <media/v4l2-async.h>  #include <media/v4l2-device.h>  #include <media/v4l2-ctrls.h> -#include <media/v4l2-of.h>  #include "adv7343_regs.h" @@ -409,7 +410,7 @@ adv7343_get_pdata(struct i2c_client *client)  	if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node)  		return client->dev.platform_data; -	np = v4l2_of_get_next_endpoint(client->dev.of_node, NULL); +	np = of_graph_get_next_endpoint(client->dev.of_node, NULL);  	if (!np)  		return NULL; 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;  	} diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index fbfdd2fc2a3..1778d320272 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -27,19 +27,21 @@   * REF_03 - Analog devices, ADV7604, Hardware Manual, Rev. F, August 2010   */ - +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h>  #include <linux/kernel.h>  #include <linux/module.h>  #include <linux/slab.h> -#include <linux/i2c.h> -#include <linux/delay.h> +#include <linux/v4l2-dv-timings.h>  #include <linux/videodev2.h>  #include <linux/workqueue.h> -#include <linux/v4l2-dv-timings.h> -#include <media/v4l2-device.h> + +#include <media/adv7604.h>  #include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h>  #include <media/v4l2-dv-timings.h> -#include <media/adv7604.h> +#include <media/v4l2-of.h>  static int debug;  module_param(debug, int, 0644); @@ -53,7 +55,75 @@ MODULE_LICENSE("GPL");  /* ADV7604 system clock frequency */  #define ADV7604_fsc (28636360) -#define DIGITAL_INPUT (state->mode == ADV7604_MODE_HDMI) +#define ADV7604_RGB_OUT					(1 << 1) + +#define ADV7604_OP_FORMAT_SEL_8BIT			(0 << 0) +#define ADV7604_OP_FORMAT_SEL_10BIT			(1 << 0) +#define ADV7604_OP_FORMAT_SEL_12BIT			(2 << 0) + +#define ADV7604_OP_MODE_SEL_SDR_422			(0 << 5) +#define ADV7604_OP_MODE_SEL_DDR_422			(1 << 5) +#define ADV7604_OP_MODE_SEL_SDR_444			(2 << 5) +#define ADV7604_OP_MODE_SEL_DDR_444			(3 << 5) +#define ADV7604_OP_MODE_SEL_SDR_422_2X			(4 << 5) +#define ADV7604_OP_MODE_SEL_ADI_CM			(5 << 5) + +#define ADV7604_OP_CH_SEL_GBR				(0 << 5) +#define ADV7604_OP_CH_SEL_GRB				(1 << 5) +#define ADV7604_OP_CH_SEL_BGR				(2 << 5) +#define ADV7604_OP_CH_SEL_RGB				(3 << 5) +#define ADV7604_OP_CH_SEL_BRG				(4 << 5) +#define ADV7604_OP_CH_SEL_RBG				(5 << 5) + +#define ADV7604_OP_SWAP_CB_CR				(1 << 0) + +enum adv7604_type { +	ADV7604, +	ADV7611, +}; + +struct adv7604_reg_seq { +	unsigned int reg; +	u8 val; +}; + +struct adv7604_format_info { +	enum v4l2_mbus_pixelcode code; +	u8 op_ch_sel; +	bool rgb_out; +	bool swap_cb_cr; +	u8 op_format_sel; +}; + +struct adv7604_chip_info { +	enum adv7604_type type; + +	bool has_afe; +	unsigned int max_port; +	unsigned int num_dv_ports; + +	unsigned int edid_enable_reg; +	unsigned int edid_status_reg; +	unsigned int lcf_reg; + +	unsigned int cable_det_mask; +	unsigned int tdms_lock_mask; +	unsigned int fmt_change_digital_mask; + +	const struct adv7604_format_info *formats; +	unsigned int nformats; + +	void (*set_termination)(struct v4l2_subdev *sd, bool enable); +	void (*setup_irqs)(struct v4l2_subdev *sd); +	unsigned int (*read_hdmi_pixelclock)(struct v4l2_subdev *sd); +	unsigned int (*read_cable_det)(struct v4l2_subdev *sd); + +	/* 0 = AFE, 1 = HDMI */ +	const struct adv7604_reg_seq *recommended_settings[2]; +	unsigned int num_recommended_settings[2]; + +	unsigned long page_mask; +};  /*   ********************************************************************** @@ -62,36 +132,38 @@ MODULE_LICENSE("GPL");   *   **********************************************************************   */ +  struct adv7604_state { +	const struct adv7604_chip_info *info;  	struct adv7604_platform_data pdata; + +	struct gpio_desc *hpd_gpio[4]; +  	struct v4l2_subdev sd; -	struct media_pad pad; +	struct media_pad pads[ADV7604_PAD_MAX]; +	unsigned int source_pad; +  	struct v4l2_ctrl_handler hdl; -	enum adv7604_mode mode; + +	enum adv7604_pad selected_input; +  	struct v4l2_dv_timings timings; -	u8 edid[256]; -	unsigned edid_blocks; +	const struct adv7604_format_info *format; + +	struct { +		u8 edid[256]; +		u32 present; +		unsigned blocks; +	} edid; +	u16 spa_port_a[2];  	struct v4l2_fract aspect_ratio;  	u32 rgb_quantization_range;  	struct workqueue_struct *work_queues;  	struct delayed_work delayed_work_enable_hotplug; -	bool connector_hdmi;  	bool restart_stdi_once; -	u32 prev_input_status;  	/* i2c clients */ -	struct i2c_client *i2c_avlink; -	struct i2c_client *i2c_cec; -	struct i2c_client *i2c_infoframe; -	struct i2c_client *i2c_esdp; -	struct i2c_client *i2c_dpp; -	struct i2c_client *i2c_afe; -	struct i2c_client *i2c_repeater; -	struct i2c_client *i2c_edid; -	struct i2c_client *i2c_hdmi; -	struct i2c_client *i2c_test; -	struct i2c_client *i2c_cp; -	struct i2c_client *i2c_vdp; +	struct i2c_client *i2c_clients[ADV7604_PAGE_MAX];  	/* controls */  	struct v4l2_ctrl *detect_tx_5v_ctrl; @@ -101,6 +173,11 @@ struct adv7604_state {  	struct v4l2_ctrl *rgb_quantization_range_ctrl;  }; +static bool adv7604_has_afe(struct adv7604_state *state) +{ +	return state->info->has_afe; +} +  /* Supported CEA and DMT timings */  static const struct v4l2_dv_timings adv7604_timings[] = {  	V4L2_DV_BT_CEA_720X480P59_94, @@ -160,6 +237,7 @@ static const struct v4l2_dv_timings adv7604_timings[] = {  	V4L2_DV_BT_DMT_1792X1344P60,  	V4L2_DV_BT_DMT_1856X1392P60,  	V4L2_DV_BT_DMT_1920X1200P60_RB, +	V4L2_DV_BT_DMT_1366X768P60_RB,  	V4L2_DV_BT_DMT_1366X768P60,  	V4L2_DV_BT_DMT_1920X1080P60,  	{ }, @@ -255,11 +333,6 @@ static inline struct adv7604_state *to_state(struct v4l2_subdev *sd)  	return container_of(sd, struct adv7604_state, sd);  } -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ -	return &container_of(ctrl->handler, struct adv7604_state, hdl)->sd; -} -  static inline unsigned hblanking(const struct v4l2_bt_timings *t)  {  	return V4L2_DV_BT_BLANKING_WIDTH(t); @@ -297,14 +370,18 @@ static s32 adv_smbus_read_byte_data_check(struct i2c_client *client,  	return -EIO;  } -static s32 adv_smbus_read_byte_data(struct i2c_client *client, u8 command) +static s32 adv_smbus_read_byte_data(struct adv7604_state *state, +				    enum adv7604_page page, u8 command)  { -	return adv_smbus_read_byte_data_check(client, command, true); +	return adv_smbus_read_byte_data_check(state->i2c_clients[page], +					      command, true);  } -static s32 adv_smbus_write_byte_data(struct i2c_client *client, -					u8 command, u8 value) +static s32 adv_smbus_write_byte_data(struct adv7604_state *state, +				     enum adv7604_page page, u8 command, +				     u8 value)  { +	struct i2c_client *client = state->i2c_clients[page];  	union i2c_smbus_data data;  	int err;  	int i; @@ -324,9 +401,11 @@ static s32 adv_smbus_write_byte_data(struct i2c_client *client,  	return err;  } -static s32 adv_smbus_write_i2c_block_data(struct i2c_client *client, -	       u8 command, unsigned length, const u8 *values) +static s32 adv_smbus_write_i2c_block_data(struct adv7604_state *state, +					  enum adv7604_page page, u8 command, +					  unsigned length, const u8 *values)  { +	struct i2c_client *client = state->i2c_clients[page];  	union i2c_smbus_data data;  	if (length > I2C_SMBUS_BLOCK_MAX) @@ -342,149 +421,150 @@ static s32 adv_smbus_write_i2c_block_data(struct i2c_client *client,  static inline int io_read(struct v4l2_subdev *sd, u8 reg)  { -	struct i2c_client *client = v4l2_get_subdevdata(sd); +	struct adv7604_state *state = to_state(sd); -	return adv_smbus_read_byte_data(client, reg); +	return adv_smbus_read_byte_data(state, ADV7604_PAGE_IO, reg);  }  static inline int io_write(struct v4l2_subdev *sd, u8 reg, u8 val)  { -	struct i2c_client *client = v4l2_get_subdevdata(sd); +	struct adv7604_state *state = to_state(sd); -	return adv_smbus_write_byte_data(client, reg, val); +	return adv_smbus_write_byte_data(state, ADV7604_PAGE_IO, reg, val);  } -static inline int io_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +static inline int io_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)  { -	return io_write(sd, reg, (io_read(sd, reg) & mask) | val); +	return io_write(sd, reg, (io_read(sd, reg) & ~mask) | val);  }  static inline int avlink_read(struct v4l2_subdev *sd, u8 reg)  {  	struct adv7604_state *state = to_state(sd); -	return adv_smbus_read_byte_data(state->i2c_avlink, reg); +	return adv_smbus_read_byte_data(state, ADV7604_PAGE_AVLINK, reg);  }  static inline int avlink_write(struct v4l2_subdev *sd, u8 reg, u8 val)  {  	struct adv7604_state *state = to_state(sd); -	return adv_smbus_write_byte_data(state->i2c_avlink, reg, val); +	return adv_smbus_write_byte_data(state, ADV7604_PAGE_AVLINK, reg, val);  }  static inline int cec_read(struct v4l2_subdev *sd, u8 reg)  {  	struct adv7604_state *state = to_state(sd); -	return adv_smbus_read_byte_data(state->i2c_cec, reg); +	return adv_smbus_read_byte_data(state, ADV7604_PAGE_CEC, reg);  }  static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val)  {  	struct adv7604_state *state = to_state(sd); -	return adv_smbus_write_byte_data(state->i2c_cec, reg, val); +	return adv_smbus_write_byte_data(state, ADV7604_PAGE_CEC, reg, val);  } -static inline int cec_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +static inline int cec_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)  { -	return cec_write(sd, reg, (cec_read(sd, reg) & mask) | val); +	return cec_write(sd, reg, (cec_read(sd, reg) & ~mask) | val);  }  static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg)  {  	struct adv7604_state *state = to_state(sd); -	return adv_smbus_read_byte_data(state->i2c_infoframe, reg); +	return adv_smbus_read_byte_data(state, ADV7604_PAGE_INFOFRAME, reg);  }  static inline int infoframe_write(struct v4l2_subdev *sd, u8 reg, u8 val)  {  	struct adv7604_state *state = to_state(sd); -	return adv_smbus_write_byte_data(state->i2c_infoframe, reg, val); +	return adv_smbus_write_byte_data(state, ADV7604_PAGE_INFOFRAME, +					 reg, val);  }  static inline int esdp_read(struct v4l2_subdev *sd, u8 reg)  {  	struct adv7604_state *state = to_state(sd); -	return adv_smbus_read_byte_data(state->i2c_esdp, reg); +	return adv_smbus_read_byte_data(state, ADV7604_PAGE_ESDP, reg);  }  static inline int esdp_write(struct v4l2_subdev *sd, u8 reg, u8 val)  {  	struct adv7604_state *state = to_state(sd); -	return adv_smbus_write_byte_data(state->i2c_esdp, reg, val); +	return adv_smbus_write_byte_data(state, ADV7604_PAGE_ESDP, reg, val);  }  static inline int dpp_read(struct v4l2_subdev *sd, u8 reg)  {  	struct adv7604_state *state = to_state(sd); -	return adv_smbus_read_byte_data(state->i2c_dpp, reg); +	return adv_smbus_read_byte_data(state, ADV7604_PAGE_DPP, reg);  }  static inline int dpp_write(struct v4l2_subdev *sd, u8 reg, u8 val)  {  	struct adv7604_state *state = to_state(sd); -	return adv_smbus_write_byte_data(state->i2c_dpp, reg, val); +	return adv_smbus_write_byte_data(state, ADV7604_PAGE_DPP, reg, val);  }  static inline int afe_read(struct v4l2_subdev *sd, u8 reg)  {  	struct adv7604_state *state = to_state(sd); -	return adv_smbus_read_byte_data(state->i2c_afe, reg); +	return adv_smbus_read_byte_data(state, ADV7604_PAGE_AFE, reg);  }  static inline int afe_write(struct v4l2_subdev *sd, u8 reg, u8 val)  {  	struct adv7604_state *state = to_state(sd); -	return adv_smbus_write_byte_data(state->i2c_afe, reg, val); +	return adv_smbus_write_byte_data(state, ADV7604_PAGE_AFE, reg, val);  }  static inline int rep_read(struct v4l2_subdev *sd, u8 reg)  {  	struct adv7604_state *state = to_state(sd); -	return adv_smbus_read_byte_data(state->i2c_repeater, reg); +	return adv_smbus_read_byte_data(state, ADV7604_PAGE_REP, reg);  }  static inline int rep_write(struct v4l2_subdev *sd, u8 reg, u8 val)  {  	struct adv7604_state *state = to_state(sd); -	return adv_smbus_write_byte_data(state->i2c_repeater, reg, val); +	return adv_smbus_write_byte_data(state, ADV7604_PAGE_REP, reg, val);  } -static inline int rep_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +static inline int rep_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)  { -	return rep_write(sd, reg, (rep_read(sd, reg) & mask) | val); +	return rep_write(sd, reg, (rep_read(sd, reg) & ~mask) | val);  }  static inline int edid_read(struct v4l2_subdev *sd, u8 reg)  {  	struct adv7604_state *state = to_state(sd); -	return adv_smbus_read_byte_data(state->i2c_edid, reg); +	return adv_smbus_read_byte_data(state, ADV7604_PAGE_EDID, reg);  }  static inline int edid_write(struct v4l2_subdev *sd, u8 reg, u8 val)  {  	struct adv7604_state *state = to_state(sd); -	return adv_smbus_write_byte_data(state->i2c_edid, reg, val); +	return adv_smbus_write_byte_data(state, ADV7604_PAGE_EDID, reg, val);  }  static inline int edid_read_block(struct v4l2_subdev *sd, unsigned len, u8 *val)  {  	struct adv7604_state *state = to_state(sd); -	struct i2c_client *client = state->i2c_edid; +	struct i2c_client *client = state->i2c_clients[ADV7604_PAGE_EDID];  	u8 msgbuf0[1] = { 0 };  	u8 msgbuf1[256];  	struct i2c_msg msg[2] = { @@ -507,118 +587,268 @@ static inline int edid_read_block(struct v4l2_subdev *sd, unsigned len, u8 *val)  	return 0;  } -static void adv7604_delayed_work_enable_hotplug(struct work_struct *work) -{ -	struct delayed_work *dwork = to_delayed_work(work); -	struct adv7604_state *state = container_of(dwork, struct adv7604_state, -						delayed_work_enable_hotplug); -	struct v4l2_subdev *sd = &state->sd; - -	v4l2_dbg(2, debug, sd, "%s: enable hotplug\n", __func__); - -	v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)1); -} -  static inline int edid_write_block(struct v4l2_subdev *sd,  					unsigned len, const u8 *val)  { -	struct i2c_client *client = v4l2_get_subdevdata(sd);  	struct adv7604_state *state = to_state(sd);  	int err = 0;  	int i;  	v4l2_dbg(2, debug, sd, "%s: write EDID block (%d byte)\n", __func__, len); -	v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)0); - -	/* Disables I2C access to internal EDID ram from DDC port */ -	rep_write_and_or(sd, 0x77, 0xf0, 0x0); -  	for (i = 0; !err && i < len; i += I2C_SMBUS_BLOCK_MAX) -		err = adv_smbus_write_i2c_block_data(state->i2c_edid, i, -				I2C_SMBUS_BLOCK_MAX, val + i); -	if (err) -		return err; +		err = adv_smbus_write_i2c_block_data(state, ADV7604_PAGE_EDID, +				i, I2C_SMBUS_BLOCK_MAX, val + i); +	return err; +} -	/* adv7604 calculates the checksums and enables I2C access to internal -	   EDID ram from DDC port. */ -	rep_write_and_or(sd, 0x77, 0xf0, 0x1); +static void adv7604_set_hpd(struct adv7604_state *state, unsigned int hpd) +{ +	unsigned int i; -	for (i = 0; i < 1000; i++) { -		if (rep_read(sd, 0x7d) & 1) -			break; -		mdelay(1); -	} -	if (i == 1000) { -		v4l_err(client, "error enabling edid\n"); -		return -EIO; +	for (i = 0; i < state->info->num_dv_ports; ++i) { +		if (IS_ERR(state->hpd_gpio[i])) +			continue; + +		gpiod_set_value_cansleep(state->hpd_gpio[i], hpd & BIT(i));  	} -	/* enable hotplug after 100 ms */ -	queue_delayed_work(state->work_queues, -			&state->delayed_work_enable_hotplug, HZ / 10); -	return 0; +	v4l2_subdev_notify(&state->sd, ADV7604_HOTPLUG, &hpd); +} + +static void adv7604_delayed_work_enable_hotplug(struct work_struct *work) +{ +	struct delayed_work *dwork = to_delayed_work(work); +	struct adv7604_state *state = container_of(dwork, struct adv7604_state, +						delayed_work_enable_hotplug); +	struct v4l2_subdev *sd = &state->sd; + +	v4l2_dbg(2, debug, sd, "%s: enable hotplug\n", __func__); + +	adv7604_set_hpd(state, state->edid.present);  }  static inline int hdmi_read(struct v4l2_subdev *sd, u8 reg)  {  	struct adv7604_state *state = to_state(sd); -	return adv_smbus_read_byte_data(state->i2c_hdmi, reg); +	return adv_smbus_read_byte_data(state, ADV7604_PAGE_HDMI, reg); +} + +static u16 hdmi_read16(struct v4l2_subdev *sd, u8 reg, u16 mask) +{ +	return ((hdmi_read(sd, reg) << 8) | hdmi_read(sd, reg + 1)) & mask;  }  static inline int hdmi_write(struct v4l2_subdev *sd, u8 reg, u8 val)  {  	struct adv7604_state *state = to_state(sd); -	return adv_smbus_write_byte_data(state->i2c_hdmi, reg, val); +	return adv_smbus_write_byte_data(state, ADV7604_PAGE_HDMI, reg, val); +} + +static inline int hdmi_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +{ +	return hdmi_write(sd, reg, (hdmi_read(sd, reg) & ~mask) | val);  }  static inline int test_read(struct v4l2_subdev *sd, u8 reg)  {  	struct adv7604_state *state = to_state(sd); -	return adv_smbus_read_byte_data(state->i2c_test, reg); +	return adv_smbus_read_byte_data(state, ADV7604_PAGE_TEST, reg);  }  static inline int test_write(struct v4l2_subdev *sd, u8 reg, u8 val)  {  	struct adv7604_state *state = to_state(sd); -	return adv_smbus_write_byte_data(state->i2c_test, reg, val); +	return adv_smbus_write_byte_data(state, ADV7604_PAGE_TEST, reg, val);  }  static inline int cp_read(struct v4l2_subdev *sd, u8 reg)  {  	struct adv7604_state *state = to_state(sd); -	return adv_smbus_read_byte_data(state->i2c_cp, reg); +	return adv_smbus_read_byte_data(state, ADV7604_PAGE_CP, reg); +} + +static u16 cp_read16(struct v4l2_subdev *sd, u8 reg, u16 mask) +{ +	return ((cp_read(sd, reg) << 8) | cp_read(sd, reg + 1)) & mask;  }  static inline int cp_write(struct v4l2_subdev *sd, u8 reg, u8 val)  {  	struct adv7604_state *state = to_state(sd); -	return adv_smbus_write_byte_data(state->i2c_cp, reg, val); +	return adv_smbus_write_byte_data(state, ADV7604_PAGE_CP, reg, val);  } -static inline int cp_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +static inline int cp_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)  { -	return cp_write(sd, reg, (cp_read(sd, reg) & mask) | val); +	return cp_write(sd, reg, (cp_read(sd, reg) & ~mask) | val);  }  static inline int vdp_read(struct v4l2_subdev *sd, u8 reg)  {  	struct adv7604_state *state = to_state(sd); -	return adv_smbus_read_byte_data(state->i2c_vdp, reg); +	return adv_smbus_read_byte_data(state, ADV7604_PAGE_VDP, reg);  }  static inline int vdp_write(struct v4l2_subdev *sd, u8 reg, u8 val)  {  	struct adv7604_state *state = to_state(sd); -	return adv_smbus_write_byte_data(state->i2c_vdp, reg, val); +	return adv_smbus_write_byte_data(state, ADV7604_PAGE_VDP, reg, val); +} + +#define ADV7604_REG(page, offset)	(((page) << 8) | (offset)) +#define ADV7604_REG_SEQ_TERM		0xffff + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int adv7604_read_reg(struct v4l2_subdev *sd, unsigned int reg) +{ +	struct adv7604_state *state = to_state(sd); +	unsigned int page = reg >> 8; + +	if (!(BIT(page) & state->info->page_mask)) +		return -EINVAL; + +	reg &= 0xff; + +	return adv_smbus_read_byte_data(state, page, reg); +} +#endif + +static int adv7604_write_reg(struct v4l2_subdev *sd, unsigned int reg, u8 val) +{ +	struct adv7604_state *state = to_state(sd); +	unsigned int page = reg >> 8; + +	if (!(BIT(page) & state->info->page_mask)) +		return -EINVAL; + +	reg &= 0xff; + +	return adv_smbus_write_byte_data(state, page, reg, val); +} + +static void adv7604_write_reg_seq(struct v4l2_subdev *sd, +				  const struct adv7604_reg_seq *reg_seq) +{ +	unsigned int i; + +	for (i = 0; reg_seq[i].reg != ADV7604_REG_SEQ_TERM; i++) +		adv7604_write_reg(sd, reg_seq[i].reg, reg_seq[i].val); +} + +/* ----------------------------------------------------------------------------- + * Format helpers + */ + +static const struct adv7604_format_info adv7604_formats[] = { +	{ V4L2_MBUS_FMT_RGB888_1X24, ADV7604_OP_CH_SEL_RGB, true, false, +	  ADV7604_OP_MODE_SEL_SDR_444 | ADV7604_OP_FORMAT_SEL_8BIT }, +	{ V4L2_MBUS_FMT_YUYV8_2X8, ADV7604_OP_CH_SEL_RGB, false, false, +	  ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT }, +	{ V4L2_MBUS_FMT_YVYU8_2X8, ADV7604_OP_CH_SEL_RGB, false, true, +	  ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT }, +	{ V4L2_MBUS_FMT_YUYV10_2X10, ADV7604_OP_CH_SEL_RGB, false, false, +	  ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_10BIT }, +	{ V4L2_MBUS_FMT_YVYU10_2X10, ADV7604_OP_CH_SEL_RGB, false, true, +	  ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_10BIT }, +	{ V4L2_MBUS_FMT_YUYV12_2X12, ADV7604_OP_CH_SEL_RGB, false, false, +	  ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT }, +	{ V4L2_MBUS_FMT_YVYU12_2X12, ADV7604_OP_CH_SEL_RGB, false, true, +	  ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT }, +	{ V4L2_MBUS_FMT_UYVY8_1X16, ADV7604_OP_CH_SEL_RBG, false, false, +	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, +	{ V4L2_MBUS_FMT_VYUY8_1X16, ADV7604_OP_CH_SEL_RBG, false, true, +	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, +	{ V4L2_MBUS_FMT_YUYV8_1X16, ADV7604_OP_CH_SEL_RGB, false, false, +	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, +	{ V4L2_MBUS_FMT_YVYU8_1X16, ADV7604_OP_CH_SEL_RGB, false, true, +	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, +	{ V4L2_MBUS_FMT_UYVY10_1X20, ADV7604_OP_CH_SEL_RBG, false, false, +	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT }, +	{ V4L2_MBUS_FMT_VYUY10_1X20, ADV7604_OP_CH_SEL_RBG, false, true, +	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT }, +	{ V4L2_MBUS_FMT_YUYV10_1X20, ADV7604_OP_CH_SEL_RGB, false, false, +	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT }, +	{ V4L2_MBUS_FMT_YVYU10_1X20, ADV7604_OP_CH_SEL_RGB, false, true, +	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT }, +	{ V4L2_MBUS_FMT_UYVY12_1X24, ADV7604_OP_CH_SEL_RBG, false, false, +	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, +	{ V4L2_MBUS_FMT_VYUY12_1X24, ADV7604_OP_CH_SEL_RBG, false, true, +	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, +	{ V4L2_MBUS_FMT_YUYV12_1X24, ADV7604_OP_CH_SEL_RGB, false, false, +	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, +	{ V4L2_MBUS_FMT_YVYU12_1X24, ADV7604_OP_CH_SEL_RGB, false, true, +	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, +}; + +static const struct adv7604_format_info adv7611_formats[] = { +	{ V4L2_MBUS_FMT_RGB888_1X24, ADV7604_OP_CH_SEL_RGB, true, false, +	  ADV7604_OP_MODE_SEL_SDR_444 | ADV7604_OP_FORMAT_SEL_8BIT }, +	{ V4L2_MBUS_FMT_YUYV8_2X8, ADV7604_OP_CH_SEL_RGB, false, false, +	  ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT }, +	{ V4L2_MBUS_FMT_YVYU8_2X8, ADV7604_OP_CH_SEL_RGB, false, true, +	  ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT }, +	{ V4L2_MBUS_FMT_YUYV12_2X12, ADV7604_OP_CH_SEL_RGB, false, false, +	  ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT }, +	{ V4L2_MBUS_FMT_YVYU12_2X12, ADV7604_OP_CH_SEL_RGB, false, true, +	  ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT }, +	{ V4L2_MBUS_FMT_UYVY8_1X16, ADV7604_OP_CH_SEL_RBG, false, false, +	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, +	{ V4L2_MBUS_FMT_VYUY8_1X16, ADV7604_OP_CH_SEL_RBG, false, true, +	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, +	{ V4L2_MBUS_FMT_YUYV8_1X16, ADV7604_OP_CH_SEL_RGB, false, false, +	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, +	{ V4L2_MBUS_FMT_YVYU8_1X16, ADV7604_OP_CH_SEL_RGB, false, true, +	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, +	{ V4L2_MBUS_FMT_UYVY12_1X24, ADV7604_OP_CH_SEL_RBG, false, false, +	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, +	{ V4L2_MBUS_FMT_VYUY12_1X24, ADV7604_OP_CH_SEL_RBG, false, true, +	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, +	{ V4L2_MBUS_FMT_YUYV12_1X24, ADV7604_OP_CH_SEL_RGB, false, false, +	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, +	{ V4L2_MBUS_FMT_YVYU12_1X24, ADV7604_OP_CH_SEL_RGB, false, true, +	  ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, +}; + +static const struct adv7604_format_info * +adv7604_format_info(struct adv7604_state *state, enum v4l2_mbus_pixelcode code) +{ +	unsigned int i; + +	for (i = 0; i < state->info->nformats; ++i) { +		if (state->info->formats[i].code == code) +			return &state->info->formats[i]; +	} + +	return NULL; +} + +/* ----------------------------------------------------------------------- */ + +static inline bool is_analog_input(struct v4l2_subdev *sd) +{ +	struct adv7604_state *state = to_state(sd); + +	return state->selected_input == ADV7604_PAD_VGA_RGB || +	       state->selected_input == ADV7604_PAD_VGA_COMP; +} + +static inline bool is_digital_input(struct v4l2_subdev *sd) +{ +	struct adv7604_state *state = to_state(sd); + +	return state->selected_input == ADV7604_PAD_HDMI_PORT_A || +	       state->selected_input == ADV7604_PAD_HDMI_PORT_B || +	       state->selected_input == ADV7604_PAD_HDMI_PORT_C || +	       state->selected_input == ADV7604_PAD_HDMI_PORT_D;  }  /* ----------------------------------------------------------------------- */ @@ -644,114 +874,61 @@ static void adv7604_inv_register(struct v4l2_subdev *sd)  static int adv7604_g_register(struct v4l2_subdev *sd,  					struct v4l2_dbg_register *reg)  { -	reg->size = 1; -	switch (reg->reg >> 8) { -	case 0: -		reg->val = io_read(sd, reg->reg & 0xff); -		break; -	case 1: -		reg->val = avlink_read(sd, reg->reg & 0xff); -		break; -	case 2: -		reg->val = cec_read(sd, reg->reg & 0xff); -		break; -	case 3: -		reg->val = infoframe_read(sd, reg->reg & 0xff); -		break; -	case 4: -		reg->val = esdp_read(sd, reg->reg & 0xff); -		break; -	case 5: -		reg->val = dpp_read(sd, reg->reg & 0xff); -		break; -	case 6: -		reg->val = afe_read(sd, reg->reg & 0xff); -		break; -	case 7: -		reg->val = rep_read(sd, reg->reg & 0xff); -		break; -	case 8: -		reg->val = edid_read(sd, reg->reg & 0xff); -		break; -	case 9: -		reg->val = hdmi_read(sd, reg->reg & 0xff); -		break; -	case 0xa: -		reg->val = test_read(sd, reg->reg & 0xff); -		break; -	case 0xb: -		reg->val = cp_read(sd, reg->reg & 0xff); -		break; -	case 0xc: -		reg->val = vdp_read(sd, reg->reg & 0xff); -		break; -	default: +	int ret; + +	ret = adv7604_read_reg(sd, reg->reg); +	if (ret < 0) {  		v4l2_info(sd, "Register %03llx not supported\n", reg->reg);  		adv7604_inv_register(sd); -		break; +		return ret;  	} + +	reg->size = 1; +	reg->val = ret; +  	return 0;  }  static int adv7604_s_register(struct v4l2_subdev *sd,  					const struct v4l2_dbg_register *reg)  { -	switch (reg->reg >> 8) { -	case 0: -		io_write(sd, reg->reg & 0xff, reg->val & 0xff); -		break; -	case 1: -		avlink_write(sd, reg->reg & 0xff, reg->val & 0xff); -		break; -	case 2: -		cec_write(sd, reg->reg & 0xff, reg->val & 0xff); -		break; -	case 3: -		infoframe_write(sd, reg->reg & 0xff, reg->val & 0xff); -		break; -	case 4: -		esdp_write(sd, reg->reg & 0xff, reg->val & 0xff); -		break; -	case 5: -		dpp_write(sd, reg->reg & 0xff, reg->val & 0xff); -		break; -	case 6: -		afe_write(sd, reg->reg & 0xff, reg->val & 0xff); -		break; -	case 7: -		rep_write(sd, reg->reg & 0xff, reg->val & 0xff); -		break; -	case 8: -		edid_write(sd, reg->reg & 0xff, reg->val & 0xff); -		break; -	case 9: -		hdmi_write(sd, reg->reg & 0xff, reg->val & 0xff); -		break; -	case 0xa: -		test_write(sd, reg->reg & 0xff, reg->val & 0xff); -		break; -	case 0xb: -		cp_write(sd, reg->reg & 0xff, reg->val & 0xff); -		break; -	case 0xc: -		vdp_write(sd, reg->reg & 0xff, reg->val & 0xff); -		break; -	default: +	int ret; + +	ret = adv7604_write_reg(sd, reg->reg, reg->val); +	if (ret < 0) {  		v4l2_info(sd, "Register %03llx not supported\n", reg->reg);  		adv7604_inv_register(sd); -		break; +		return ret;  	} +  	return 0;  }  #endif +static unsigned int adv7604_read_cable_det(struct v4l2_subdev *sd) +{ +	u8 value = io_read(sd, 0x6f); + +	return ((value & 0x10) >> 4) +	     | ((value & 0x08) >> 2) +	     | ((value & 0x04) << 0) +	     | ((value & 0x02) << 2); +} + +static unsigned int adv7611_read_cable_det(struct v4l2_subdev *sd) +{ +	u8 value = io_read(sd, 0x6f); + +	return value & 1; +} +  static int adv7604_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd)  {  	struct adv7604_state *state = to_state(sd); +	const struct adv7604_chip_info *info = state->info; -	/* port A only */  	return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, -				((io_read(sd, 0x6f) & 0x10) >> 4)); +				info->read_cable_det(sd));  }  static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd, @@ -759,12 +936,11 @@ static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd,  		const struct adv7604_video_standards *predef_vid_timings,  		const struct v4l2_dv_timings *timings)  { -	struct adv7604_state *state = to_state(sd);  	int i;  	for (i = 0; predef_vid_timings[i].timings.bt.width; i++) {  		if (!v4l2_match_dv_timings(timings, &predef_vid_timings[i].timings, -					DIGITAL_INPUT ? 250000 : 1000000)) +					is_digital_input(sd) ? 250000 : 1000000))  			continue;  		io_write(sd, 0x00, predef_vid_timings[i].vid_std); /* video std */  		io_write(sd, 0x01, (predef_vid_timings[i].v_freq << 4) + @@ -783,11 +959,13 @@ static int configure_predefined_video_timings(struct v4l2_subdev *sd,  	v4l2_dbg(1, debug, sd, "%s", __func__); -	/* reset to default values */ -	io_write(sd, 0x16, 0x43); -	io_write(sd, 0x17, 0x5a); +	if (adv7604_has_afe(state)) { +		/* reset to default values */ +		io_write(sd, 0x16, 0x43); +		io_write(sd, 0x17, 0x5a); +	}  	/* disable embedded syncs for auto graphics mode */ -	cp_write_and_or(sd, 0x81, 0xef, 0x00); +	cp_write_clr_set(sd, 0x81, 0x10, 0x00);  	cp_write(sd, 0x8f, 0x00);  	cp_write(sd, 0x90, 0x00);  	cp_write(sd, 0xa2, 0x00); @@ -799,27 +977,22 @@ static int configure_predefined_video_timings(struct v4l2_subdev *sd,  	cp_write(sd, 0xab, 0x00);  	cp_write(sd, 0xac, 0x00); -	switch (state->mode) { -	case ADV7604_MODE_COMP: -	case ADV7604_MODE_GR: +	if (is_analog_input(sd)) {  		err = find_and_set_predefined_video_timings(sd,  				0x01, adv7604_prim_mode_comp, timings);  		if (err)  			err = find_and_set_predefined_video_timings(sd,  					0x02, adv7604_prim_mode_gr, timings); -		break; -	case ADV7604_MODE_HDMI: +	} else if (is_digital_input(sd)) {  		err = find_and_set_predefined_video_timings(sd,  				0x05, adv7604_prim_mode_hdmi_comp, timings);  		if (err)  			err = find_and_set_predefined_video_timings(sd,  					0x06, adv7604_prim_mode_hdmi_gr, timings); -		break; -	default: -		v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", -				__func__, state->mode); +	} else { +		v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n", +				__func__, state->selected_input);  		err = -1; -		break;  	} @@ -830,7 +1003,6 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd,  		const struct v4l2_bt_timings *bt)  {  	struct adv7604_state *state = to_state(sd); -	struct i2c_client *client = v4l2_get_subdevdata(sd);  	u32 width = htotal(bt);  	u32 height = vtotal(bt);  	u16 cp_start_sav = bt->hsync + bt->hbackporch - 4; @@ -846,45 +1018,39 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd,  	v4l2_dbg(2, debug, sd, "%s\n", __func__); -	switch (state->mode) { -	case ADV7604_MODE_COMP: -	case ADV7604_MODE_GR: +	if (is_analog_input(sd)) {  		/* auto graphics */  		io_write(sd, 0x00, 0x07); /* video std */  		io_write(sd, 0x01, 0x02); /* prim mode */  		/* enable embedded syncs for auto graphics mode */ -		cp_write_and_or(sd, 0x81, 0xef, 0x10); +		cp_write_clr_set(sd, 0x81, 0x10, 0x10);  		/* Should only be set in auto-graphics mode [REF_02, p. 91-92] */  		/* setup PLL_DIV_MAN_EN and PLL_DIV_RATIO */  		/* IO-map reg. 0x16 and 0x17 should be written in sequence */ -		if (adv_smbus_write_i2c_block_data(client, 0x16, 2, pll)) { +		if (adv_smbus_write_i2c_block_data(state, ADV7604_PAGE_IO, +						   0x16, 2, pll))  			v4l2_err(sd, "writing to reg 0x16 and 0x17 failed\n"); -			break; -		}  		/* active video - horizontal timing */  		cp_write(sd, 0xa2, (cp_start_sav >> 4) & 0xff);  		cp_write(sd, 0xa3, ((cp_start_sav & 0x0f) << 4) | -					((cp_start_eav >> 8) & 0x0f)); +				   ((cp_start_eav >> 8) & 0x0f));  		cp_write(sd, 0xa4, cp_start_eav & 0xff);  		/* active video - vertical timing */  		cp_write(sd, 0xa5, (cp_start_vbi >> 4) & 0xff);  		cp_write(sd, 0xa6, ((cp_start_vbi & 0xf) << 4) | -					((cp_end_vbi >> 8) & 0xf)); +				   ((cp_end_vbi >> 8) & 0xf));  		cp_write(sd, 0xa7, cp_end_vbi & 0xff); -		break; -	case ADV7604_MODE_HDMI: +	} else if (is_digital_input(sd)) {  		/* set default prim_mode/vid_std for HDMI -		   accoring to [REF_03, c. 4.2] */ +		   according to [REF_03, c. 4.2] */  		io_write(sd, 0x00, 0x02); /* video std */  		io_write(sd, 0x01, 0x06); /* prim mode */ -		break; -	default: -		v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", -				__func__, state->mode); -		break; +	} else { +		v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n", +				__func__, state->selected_input);  	}  	cp_write(sd, 0x8f, (ch1_fr_ll >> 8) & 0x7); @@ -893,46 +1059,156 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd,  	cp_write(sd, 0xac, (height & 0x0f) << 4);  } +static void adv7604_set_offset(struct v4l2_subdev *sd, bool auto_offset, u16 offset_a, u16 offset_b, u16 offset_c) +{ +	struct adv7604_state *state = to_state(sd); +	u8 offset_buf[4]; + +	if (auto_offset) { +		offset_a = 0x3ff; +		offset_b = 0x3ff; +		offset_c = 0x3ff; +	} + +	v4l2_dbg(2, debug, sd, "%s: %s offset: a = 0x%x, b = 0x%x, c = 0x%x\n", +			__func__, auto_offset ? "Auto" : "Manual", +			offset_a, offset_b, offset_c); + +	offset_buf[0] = (cp_read(sd, 0x77) & 0xc0) | ((offset_a & 0x3f0) >> 4); +	offset_buf[1] = ((offset_a & 0x00f) << 4) | ((offset_b & 0x3c0) >> 6); +	offset_buf[2] = ((offset_b & 0x03f) << 2) | ((offset_c & 0x300) >> 8); +	offset_buf[3] = offset_c & 0x0ff; + +	/* Registers must be written in this order with no i2c access in between */ +	if (adv_smbus_write_i2c_block_data(state, ADV7604_PAGE_CP, +					   0x77, 4, offset_buf)) +		v4l2_err(sd, "%s: i2c error writing to CP reg 0x77, 0x78, 0x79, 0x7a\n", __func__); +} + +static void adv7604_set_gain(struct v4l2_subdev *sd, bool auto_gain, u16 gain_a, u16 gain_b, u16 gain_c) +{ +	struct adv7604_state *state = to_state(sd); +	u8 gain_buf[4]; +	u8 gain_man = 1; +	u8 agc_mode_man = 1; + +	if (auto_gain) { +		gain_man = 0; +		agc_mode_man = 0; +		gain_a = 0x100; +		gain_b = 0x100; +		gain_c = 0x100; +	} + +	v4l2_dbg(2, debug, sd, "%s: %s gain: a = 0x%x, b = 0x%x, c = 0x%x\n", +			__func__, auto_gain ? "Auto" : "Manual", +			gain_a, gain_b, gain_c); + +	gain_buf[0] = ((gain_man << 7) | (agc_mode_man << 6) | ((gain_a & 0x3f0) >> 4)); +	gain_buf[1] = (((gain_a & 0x00f) << 4) | ((gain_b & 0x3c0) >> 6)); +	gain_buf[2] = (((gain_b & 0x03f) << 2) | ((gain_c & 0x300) >> 8)); +	gain_buf[3] = ((gain_c & 0x0ff)); + +	/* Registers must be written in this order with no i2c access in between */ +	if (adv_smbus_write_i2c_block_data(state, ADV7604_PAGE_CP, +					   0x73, 4, gain_buf)) +		v4l2_err(sd, "%s: i2c error writing to CP reg 0x73, 0x74, 0x75, 0x76\n", __func__); +} +  static void set_rgb_quantization_range(struct v4l2_subdev *sd)  {  	struct adv7604_state *state = to_state(sd); +	bool rgb_output = io_read(sd, 0x02) & 0x02; +	bool hdmi_signal = hdmi_read(sd, 0x05) & 0x80; + +	v4l2_dbg(2, debug, sd, "%s: RGB quantization range: %d, RGB out: %d, HDMI: %d\n", +			__func__, state->rgb_quantization_range, +			rgb_output, hdmi_signal); + +	adv7604_set_gain(sd, true, 0x0, 0x0, 0x0); +	adv7604_set_offset(sd, true, 0x0, 0x0, 0x0);  	switch (state->rgb_quantization_range) {  	case V4L2_DV_RGB_RANGE_AUTO: -		/* automatic */ -		if (DIGITAL_INPUT && !(hdmi_read(sd, 0x05) & 0x80)) { -			/* receiving DVI-D signal */ +		if (state->selected_input == ADV7604_PAD_VGA_RGB) { +			/* Receiving analog RGB signal +			 * Set RGB full range (0-255) */ +			io_write_clr_set(sd, 0x02, 0xf0, 0x10); +			break; +		} + +		if (state->selected_input == ADV7604_PAD_VGA_COMP) { +			/* Receiving analog YPbPr signal +			 * Set automode */ +			io_write_clr_set(sd, 0x02, 0xf0, 0xf0); +			break; +		} + +		if (hdmi_signal) { +			/* Receiving HDMI signal +			 * Set automode */ +			io_write_clr_set(sd, 0x02, 0xf0, 0xf0); +			break; +		} -			/* ADV7604 selects RGB limited range regardless of -			   input format (CE/IT) in automatic mode */ -			if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) { -				/* RGB limited range (16-235) */ -				io_write_and_or(sd, 0x02, 0x0f, 0x00); +		/* Receiving DVI-D signal +		 * ADV7604 selects RGB limited range regardless of +		 * input format (CE/IT) in automatic mode */ +		if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) { +			/* RGB limited range (16-235) */ +			io_write_clr_set(sd, 0x02, 0xf0, 0x00); +		} else { +			/* RGB full range (0-255) */ +			io_write_clr_set(sd, 0x02, 0xf0, 0x10); +			if (is_digital_input(sd) && rgb_output) { +				adv7604_set_offset(sd, false, 0x40, 0x40, 0x40);  			} else { -				/* RGB full range (0-255) */ -				io_write_and_or(sd, 0x02, 0x0f, 0x10); +				adv7604_set_gain(sd, false, 0xe0, 0xe0, 0xe0); +				adv7604_set_offset(sd, false, 0x70, 0x70, 0x70);  			} -		} else { -			/* receiving HDMI or analog signal, set automode */ -			io_write_and_or(sd, 0x02, 0x0f, 0xf0);  		}  		break;  	case V4L2_DV_RGB_RANGE_LIMITED: +		if (state->selected_input == ADV7604_PAD_VGA_COMP) { +			/* YCrCb limited range (16-235) */ +			io_write_clr_set(sd, 0x02, 0xf0, 0x20); +			break; +		} +  		/* RGB limited range (16-235) */ -		io_write_and_or(sd, 0x02, 0x0f, 0x00); +		io_write_clr_set(sd, 0x02, 0xf0, 0x00); +  		break;  	case V4L2_DV_RGB_RANGE_FULL: +		if (state->selected_input == ADV7604_PAD_VGA_COMP) { +			/* YCrCb full range (0-255) */ +			io_write_clr_set(sd, 0x02, 0xf0, 0x60); +			break; +		} +  		/* RGB full range (0-255) */ -		io_write_and_or(sd, 0x02, 0x0f, 0x10); +		io_write_clr_set(sd, 0x02, 0xf0, 0x10); + +		if (is_analog_input(sd) || hdmi_signal) +			break; + +		/* Adjust gain/offset for DVI-D signals only */ +		if (rgb_output) { +			adv7604_set_offset(sd, false, 0x40, 0x40, 0x40); +		} else { +			adv7604_set_gain(sd, false, 0xe0, 0xe0, 0xe0); +			adv7604_set_offset(sd, false, 0x70, 0x70, 0x70); +		}  		break;  	}  } -  static int adv7604_s_ctrl(struct v4l2_ctrl *ctrl)  { -	struct v4l2_subdev *sd = to_sd(ctrl); +	struct v4l2_subdev *sd = +		&container_of(ctrl->handler, struct adv7604_state, hdl)->sd; +  	struct adv7604_state *state = to_state(sd);  	switch (ctrl->id) { @@ -953,6 +1229,8 @@ static int adv7604_s_ctrl(struct v4l2_ctrl *ctrl)  		set_rgb_quantization_range(sd);  		return 0;  	case V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE: +		if (!adv7604_has_afe(state)) +			return -EINVAL;  		/* Set the analog sampling phase. This is needed to find the  		   best sampling phase for analog video: an application or  		   driver has to try a number of phases and analyze the picture @@ -962,7 +1240,7 @@ static int adv7604_s_ctrl(struct v4l2_ctrl *ctrl)  	case V4L2_CID_ADV_RX_FREE_RUN_COLOR_MANUAL:  		/* Use the default blue color for free running mode,  		   or supply your own. */ -		cp_write_and_or(sd, 0xbf, ~0x04, (ctrl->val << 2)); +		cp_write_clr_set(sd, 0xbf, 0x04, ctrl->val << 2);  		return 0;  	case V4L2_CID_ADV_RX_FREE_RUN_COLOR:  		cp_write(sd, 0xc0, (ctrl->val & 0xff0000) >> 16); @@ -983,13 +1261,17 @@ static inline bool no_power(struct v4l2_subdev *sd)  static inline bool no_signal_tmds(struct v4l2_subdev *sd)  { -	/* TODO port B, C and D */ -	return !(io_read(sd, 0x6a) & 0x10); +	struct adv7604_state *state = to_state(sd); + +	return !(io_read(sd, 0x6a) & (0x10 >> state->selected_input));  }  static inline bool no_lock_tmds(struct v4l2_subdev *sd)  { -	return (io_read(sd, 0x6a) & 0xe0) != 0xe0; +	struct adv7604_state *state = to_state(sd); +	const struct adv7604_chip_info *info = state->info; + +	return (io_read(sd, 0x6a) & info->tdms_lock_mask) != info->tdms_lock_mask;  }  static inline bool is_hdmi(struct v4l2_subdev *sd) @@ -999,6 +1281,15 @@ static inline bool is_hdmi(struct v4l2_subdev *sd)  static inline bool no_lock_sspd(struct v4l2_subdev *sd)  { +	struct adv7604_state *state = to_state(sd); + +	/* +	 * Chips without a AFE don't expose registers for the SSPD, so just assume +	 * that we have a lock. +	 */ +	if (adv7604_has_afe(state)) +		return false; +  	/* TODO channel 2 */  	return ((cp_read(sd, 0xb5) & 0xd0) != 0xd0);  } @@ -1011,7 +1302,6 @@ static inline bool no_lock_stdi(struct v4l2_subdev *sd)  static inline bool no_signal(struct v4l2_subdev *sd)  { -	struct adv7604_state *state = to_state(sd);  	bool ret;  	ret = no_power(sd); @@ -1019,7 +1309,7 @@ static inline bool no_signal(struct v4l2_subdev *sd)  	ret |= no_lock_stdi(sd);  	ret |= no_lock_sspd(sd); -	if (DIGITAL_INPUT) { +	if (is_digital_input(sd)) {  		ret |= no_lock_tmds(sd);  		ret |= no_signal_tmds(sd);  	} @@ -1029,6 +1319,11 @@ static inline bool no_signal(struct v4l2_subdev *sd)  static inline bool no_lock_cp(struct v4l2_subdev *sd)  { +	struct adv7604_state *state = to_state(sd); + +	if (!adv7604_has_afe(state)) +		return false; +  	/* CP has detected a non standard number of lines on the incoming  	   video compared to what it is configured to receive by s_dv_timings */  	return io_read(sd, 0x12) & 0x01; @@ -1036,13 +1331,11 @@ static inline bool no_lock_cp(struct v4l2_subdev *sd)  static int adv7604_g_input_status(struct v4l2_subdev *sd, u32 *status)  { -	struct adv7604_state *state = to_state(sd); -  	*status = 0;  	*status |= no_power(sd) ? V4L2_IN_ST_NO_POWER : 0;  	*status |= no_signal(sd) ? V4L2_IN_ST_NO_SIGNAL : 0;  	if (no_lock_cp(sd)) -		*status |= DIGITAL_INPUT ? V4L2_IN_ST_NO_SYNC : V4L2_IN_ST_NO_H_LOCK; +		*status |= is_digital_input(sd) ? V4L2_IN_ST_NO_SYNC : V4L2_IN_ST_NO_H_LOCK;  	v4l2_dbg(1, debug, sd, "%s: status = 0x%x\n", __func__, *status); @@ -1099,28 +1392,40 @@ static int stdi2dv_timings(struct v4l2_subdev *sd,  	return -1;  } +  static int read_stdi(struct v4l2_subdev *sd, struct stdi_readback *stdi)  { +	struct adv7604_state *state = to_state(sd); +	const struct adv7604_chip_info *info = state->info; +	u8 polarity; +  	if (no_lock_stdi(sd) || no_lock_sspd(sd)) {  		v4l2_dbg(2, debug, sd, "%s: STDI and/or SSPD not locked\n", __func__);  		return -1;  	}  	/* read STDI */ -	stdi->bl = ((cp_read(sd, 0xb1) & 0x3f) << 8) | cp_read(sd, 0xb2); -	stdi->lcf = ((cp_read(sd, 0xb3) & 0x7) << 8) | cp_read(sd, 0xb4); +	stdi->bl = cp_read16(sd, 0xb1, 0x3fff); +	stdi->lcf = cp_read16(sd, info->lcf_reg, 0x7ff);  	stdi->lcvs = cp_read(sd, 0xb3) >> 3;  	stdi->interlaced = io_read(sd, 0x12) & 0x10; -	/* read SSPD */ -	if ((cp_read(sd, 0xb5) & 0x03) == 0x01) { -		stdi->hs_pol = ((cp_read(sd, 0xb5) & 0x10) ? -				((cp_read(sd, 0xb5) & 0x08) ? '+' : '-') : 'x'); -		stdi->vs_pol = ((cp_read(sd, 0xb5) & 0x40) ? -				((cp_read(sd, 0xb5) & 0x20) ? '+' : '-') : 'x'); +	if (adv7604_has_afe(state)) { +		/* read SSPD */ +		polarity = cp_read(sd, 0xb5); +		if ((polarity & 0x03) == 0x01) { +			stdi->hs_pol = polarity & 0x10 +				     ? (polarity & 0x08 ? '+' : '-') : 'x'; +			stdi->vs_pol = polarity & 0x40 +				     ? (polarity & 0x20 ? '+' : '-') : 'x'; +		} else { +			stdi->hs_pol = 'x'; +			stdi->vs_pol = 'x'; +		}  	} else { -		stdi->hs_pol = 'x'; -		stdi->vs_pol = 'x'; +		polarity = hdmi_read(sd, 0x05); +		stdi->hs_pol = polarity & 0x20 ? '+' : '-'; +		stdi->vs_pol = polarity & 0x10 ? '+' : '-';  	}  	if (no_lock_stdi(sd) || no_lock_sspd(sd)) { @@ -1147,8 +1452,14 @@ static int read_stdi(struct v4l2_subdev *sd, struct stdi_readback *stdi)  static int adv7604_enum_dv_timings(struct v4l2_subdev *sd,  			struct v4l2_enum_dv_timings *timings)  { +	struct adv7604_state *state = to_state(sd); +  	if (timings->index >= ARRAY_SIZE(adv7604_timings) - 1)  		return -EINVAL; + +	if (timings->pad >= state->source_pad) +		return -EINVAL; +  	memset(timings->reserved, 0, sizeof(timings->reserved));  	timings->timings = adv7604_timings[timings->index];  	return 0; @@ -1159,14 +1470,28 @@ static int adv7604_dv_timings_cap(struct v4l2_subdev *sd,  {  	struct adv7604_state *state = to_state(sd); +	if (cap->pad >= state->source_pad) +		return -EINVAL; +  	cap->type = V4L2_DV_BT_656_1120;  	cap->bt.max_width = 1920;  	cap->bt.max_height = 1200;  	cap->bt.min_pixelclock = 25000000; -	if (DIGITAL_INPUT) + +	switch (cap->pad) { +	case ADV7604_PAD_HDMI_PORT_A: +	case ADV7604_PAD_HDMI_PORT_B: +	case ADV7604_PAD_HDMI_PORT_C: +	case ADV7604_PAD_HDMI_PORT_D:  		cap->bt.max_pixelclock = 225000000; -	else +		break; +	case ADV7604_PAD_VGA_RGB: +	case ADV7604_PAD_VGA_COMP: +	default:  		cap->bt.max_pixelclock = 170000000; +		break; +	} +  	cap->bt.standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |  			 V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT;  	cap->bt.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | @@ -1179,22 +1504,54 @@ static int adv7604_dv_timings_cap(struct v4l2_subdev *sd,  static void adv7604_fill_optional_dv_timings_fields(struct v4l2_subdev *sd,  		struct v4l2_dv_timings *timings)  { -	struct adv7604_state *state = to_state(sd);  	int i;  	for (i = 0; adv7604_timings[i].bt.width; i++) {  		if (v4l2_match_dv_timings(timings, &adv7604_timings[i], -					DIGITAL_INPUT ? 250000 : 1000000)) { +					is_digital_input(sd) ? 250000 : 1000000)) {  			*timings = adv7604_timings[i];  			break;  		}  	}  } +static unsigned int adv7604_read_hdmi_pixelclock(struct v4l2_subdev *sd) +{ +	unsigned int freq; +	int a, b; + +	a = hdmi_read(sd, 0x06); +	b = hdmi_read(sd, 0x3b); +	if (a < 0 || b < 0) +		return 0; +	freq =  a * 1000000 + ((b & 0x30) >> 4) * 250000; + +	if (is_hdmi(sd)) { +		/* adjust for deep color mode */ +		unsigned bits_per_channel = ((hdmi_read(sd, 0x0b) & 0x60) >> 4) + 8; + +		freq = freq * 8 / bits_per_channel; +	} + +	return freq; +} + +static unsigned int adv7611_read_hdmi_pixelclock(struct v4l2_subdev *sd) +{ +	int a, b; + +	a = hdmi_read(sd, 0x51); +	b = hdmi_read(sd, 0x52); +	if (a < 0 || b < 0) +		return 0; +	return ((a << 1) | (b >> 7)) * 1000000 + (b & 0x7f) * 1000000 / 128; +} +  static int adv7604_query_dv_timings(struct v4l2_subdev *sd,  			struct v4l2_dv_timings *timings)  {  	struct adv7604_state *state = to_state(sd); +	const struct adv7604_chip_info *info = state->info;  	struct v4l2_bt_timings *bt = &timings->bt;  	struct stdi_readback stdi; @@ -1204,6 +1561,7 @@ static int adv7604_query_dv_timings(struct v4l2_subdev *sd,  	memset(timings, 0, sizeof(struct v4l2_dv_timings));  	if (no_signal(sd)) { +		state->restart_stdi_once = true;  		v4l2_dbg(1, debug, sd, "%s: no valid signal\n", __func__);  		return -ENOLINK;  	} @@ -1216,45 +1574,26 @@ static int adv7604_query_dv_timings(struct v4l2_subdev *sd,  	bt->interlaced = stdi.interlaced ?  		V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; -	if (DIGITAL_INPUT) { -		uint32_t freq; - +	if (is_digital_input(sd)) {  		timings->type = V4L2_DV_BT_656_1120; -		bt->width = (hdmi_read(sd, 0x07) & 0x0f) * 256 + hdmi_read(sd, 0x08); -		bt->height = (hdmi_read(sd, 0x09) & 0x0f) * 256 + hdmi_read(sd, 0x0a); -		freq = (hdmi_read(sd, 0x06) * 1000000) + -			((hdmi_read(sd, 0x3b) & 0x30) >> 4) * 250000; -		if (is_hdmi(sd)) { -			/* adjust for deep color mode */ -			unsigned bits_per_channel = ((hdmi_read(sd, 0x0b) & 0x60) >> 4) + 8; - -			freq = freq * 8 / bits_per_channel; -		} -		bt->pixelclock = freq; -		bt->hfrontporch = (hdmi_read(sd, 0x20) & 0x03) * 256 + -			hdmi_read(sd, 0x21); -		bt->hsync = (hdmi_read(sd, 0x22) & 0x03) * 256 + -			hdmi_read(sd, 0x23); -		bt->hbackporch = (hdmi_read(sd, 0x24) & 0x03) * 256 + -			hdmi_read(sd, 0x25); -		bt->vfrontporch = ((hdmi_read(sd, 0x2a) & 0x1f) * 256 + -			hdmi_read(sd, 0x2b)) / 2; -		bt->vsync = ((hdmi_read(sd, 0x2e) & 0x1f) * 256 + -			hdmi_read(sd, 0x2f)) / 2; -		bt->vbackporch = ((hdmi_read(sd, 0x32) & 0x1f) * 256 + -			hdmi_read(sd, 0x33)) / 2; +		/* FIXME: All masks are incorrect for ADV7611 */ +		bt->width = hdmi_read16(sd, 0x07, 0xfff); +		bt->height = hdmi_read16(sd, 0x09, 0xfff); +		bt->pixelclock = info->read_hdmi_pixelclock(sd); +		bt->hfrontporch = hdmi_read16(sd, 0x20, 0x3ff); +		bt->hsync = hdmi_read16(sd, 0x22, 0x3ff); +		bt->hbackporch = hdmi_read16(sd, 0x24, 0x3ff); +		bt->vfrontporch = hdmi_read16(sd, 0x2a, 0x1fff) / 2; +		bt->vsync = hdmi_read16(sd, 0x2e, 0x1fff) / 2; +		bt->vbackporch = hdmi_read16(sd, 0x32, 0x1fff) / 2;  		bt->polarities = ((hdmi_read(sd, 0x05) & 0x10) ? V4L2_DV_VSYNC_POS_POL : 0) |  			((hdmi_read(sd, 0x05) & 0x20) ? V4L2_DV_HSYNC_POS_POL : 0);  		if (bt->interlaced == V4L2_DV_INTERLACED) { -			bt->height += (hdmi_read(sd, 0x0b) & 0x0f) * 256 + -					hdmi_read(sd, 0x0c); -			bt->il_vfrontporch = ((hdmi_read(sd, 0x2c) & 0x1f) * 256 + -					hdmi_read(sd, 0x2d)) / 2; -			bt->il_vsync = ((hdmi_read(sd, 0x30) & 0x1f) * 256 + -					hdmi_read(sd, 0x31)) / 2; -			bt->vbackporch = ((hdmi_read(sd, 0x34) & 0x1f) * 256 + -					hdmi_read(sd, 0x35)) / 2; +			bt->height += hdmi_read16(sd, 0x0b, 0xfff); +			bt->il_vfrontporch = hdmi_read16(sd, 0x2c, 0x1fff) / 2; +			bt->il_vsync = hdmi_read16(sd, 0x30, 0x1fff) / 2; +			bt->vbackporch = hdmi_read16(sd, 0x34, 0x1fff) / 2;  		}  		adv7604_fill_optional_dv_timings_fields(sd, timings);  	} else { @@ -1284,11 +1623,11 @@ static int adv7604_query_dv_timings(struct v4l2_subdev *sd,  				v4l2_dbg(1, debug, sd, "%s: restart STDI\n", __func__);  				/* TODO restart STDI for Sync Channel 2 */  				/* enter one-shot mode */ -				cp_write_and_or(sd, 0x86, 0xf9, 0x00); +				cp_write_clr_set(sd, 0x86, 0x06, 0x00);  				/* trigger STDI restart */ -				cp_write_and_or(sd, 0x86, 0xf9, 0x04); +				cp_write_clr_set(sd, 0x86, 0x06, 0x04);  				/* reset to continuous mode */ -				cp_write_and_or(sd, 0x86, 0xf9, 0x02); +				cp_write_clr_set(sd, 0x86, 0x06, 0x02);  				state->restart_stdi_once = false;  				return -ENOLINK;  			} @@ -1305,8 +1644,8 @@ found:  		return -ENOLINK;  	} -	if ((!DIGITAL_INPUT && bt->pixelclock > 170000000) || -			(DIGITAL_INPUT && bt->pixelclock > 225000000)) { +	if ((is_analog_input(sd) && bt->pixelclock > 170000000) || +			(is_digital_input(sd) && bt->pixelclock > 225000000)) {  		v4l2_dbg(1, debug, sd, "%s: pixelclock out of range %d\n",  				__func__, (u32)bt->pixelclock);  		return -ERANGE; @@ -1329,10 +1668,15 @@ static int adv7604_s_dv_timings(struct v4l2_subdev *sd,  	if (!timings)  		return -EINVAL; +	if (v4l2_match_dv_timings(&state->timings, timings, 0)) { +		v4l2_dbg(1, debug, sd, "%s: no change\n", __func__); +		return 0; +	} +  	bt = &timings->bt; -	if ((!DIGITAL_INPUT && bt->pixelclock > 170000000) || -			(DIGITAL_INPUT && bt->pixelclock > 225000000)) { +	if ((is_analog_input(sd) && bt->pixelclock > 170000000) || +			(is_digital_input(sd) && bt->pixelclock > 225000000)) {  		v4l2_dbg(1, debug, sd, "%s: pixelclock out of range %d\n",  				__func__, (u32)bt->pixelclock);  		return -ERANGE; @@ -1342,7 +1686,7 @@ static int adv7604_s_dv_timings(struct v4l2_subdev *sd,  	state->timings = *timings; -	cp_write(sd, 0x91, bt->interlaced ? 0x50 : 0x10); +	cp_write_clr_set(sd, 0x91, 0x40, bt->interlaced ? 0x40 : 0x00);  	/* Use prim_mode and vid_std when available */  	err = configure_predefined_video_timings(sd, timings); @@ -1354,7 +1698,6 @@ static int adv7604_s_dv_timings(struct v4l2_subdev *sd,  	set_rgb_quantization_range(sd); -  	if (debug > 1)  		v4l2_print_dv_timings(sd->name, "adv7604_s_dv_timings: ",  				      timings, true); @@ -1370,103 +1713,71 @@ static int adv7604_g_dv_timings(struct v4l2_subdev *sd,  	return 0;  } +static void adv7604_set_termination(struct v4l2_subdev *sd, bool enable) +{ +	hdmi_write(sd, 0x01, enable ? 0x00 : 0x78); +} + +static void adv7611_set_termination(struct v4l2_subdev *sd, bool enable) +{ +	hdmi_write(sd, 0x83, enable ? 0xfe : 0xff); +} +  static void enable_input(struct v4l2_subdev *sd)  {  	struct adv7604_state *state = to_state(sd); -	switch (state->mode) { -	case ADV7604_MODE_COMP: -	case ADV7604_MODE_GR: -		/* enable */ +	if (is_analog_input(sd)) {  		io_write(sd, 0x15, 0xb0);   /* Disable Tristate of Pins (no audio) */ -		break; -	case ADV7604_MODE_HDMI: -		/* enable */ -		hdmi_write(sd, 0x1a, 0x0a); /* Unmute audio */ -		hdmi_write(sd, 0x01, 0x00); /* Enable HDMI clock terminators */ +	} else if (is_digital_input(sd)) { +		hdmi_write_clr_set(sd, 0x00, 0x03, state->selected_input); +		state->info->set_termination(sd, true);  		io_write(sd, 0x15, 0xa0);   /* Disable Tristate of Pins */ -		break; -	default: -		v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", -				__func__, state->mode); -		break; +		hdmi_write_clr_set(sd, 0x1a, 0x10, 0x00); /* Unmute audio */ +	} else { +		v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n", +				__func__, state->selected_input);  	}  }  static void disable_input(struct v4l2_subdev *sd)  { -	/* disable */ +	struct adv7604_state *state = to_state(sd); + +	hdmi_write_clr_set(sd, 0x1a, 0x10, 0x10); /* Mute audio */ +	msleep(16); /* 512 samples with >= 32 kHz sample rate [REF_03, c. 7.16.10] */  	io_write(sd, 0x15, 0xbe);   /* Tristate all outputs from video core */ -	hdmi_write(sd, 0x1a, 0x1a); /* Mute audio */ -	hdmi_write(sd, 0x01, 0x78); /* Disable HDMI clock terminators */ +	state->info->set_termination(sd, false);  }  static void select_input(struct v4l2_subdev *sd)  {  	struct adv7604_state *state = to_state(sd); +	const struct adv7604_chip_info *info = state->info; -	switch (state->mode) { -	case ADV7604_MODE_COMP: -	case ADV7604_MODE_GR: -		/* reset ADI recommended settings for HDMI: */ -		/* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */ -		hdmi_write(sd, 0x0d, 0x04); /* HDMI filter optimization */ -		hdmi_write(sd, 0x3d, 0x00); /* DDC bus active pull-up control */ -		hdmi_write(sd, 0x3e, 0x74); /* TMDS PLL optimization */ -		hdmi_write(sd, 0x4e, 0x3b); /* TMDS PLL optimization */ -		hdmi_write(sd, 0x57, 0x74); /* TMDS PLL optimization */ -		hdmi_write(sd, 0x58, 0x63); /* TMDS PLL optimization */ -		hdmi_write(sd, 0x8d, 0x18); /* equaliser */ -		hdmi_write(sd, 0x8e, 0x34); /* equaliser */ -		hdmi_write(sd, 0x93, 0x88); /* equaliser */ -		hdmi_write(sd, 0x94, 0x2e); /* equaliser */ -		hdmi_write(sd, 0x96, 0x00); /* enable automatic EQ changing */ +	if (is_analog_input(sd)) { +		adv7604_write_reg_seq(sd, info->recommended_settings[0]);  		afe_write(sd, 0x00, 0x08); /* power up ADC */  		afe_write(sd, 0x01, 0x06); /* power up Analog Front End */  		afe_write(sd, 0xc8, 0x00); /* phase control */ +	} else if (is_digital_input(sd)) { +		hdmi_write(sd, 0x00, state->selected_input & 0x03); -		/* set ADI recommended settings for digitizer */ -		/* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */ -		afe_write(sd, 0x12, 0x7b); /* ADC noise shaping filter controls */ -		afe_write(sd, 0x0c, 0x1f); /* CP core gain controls */ -		cp_write(sd, 0x3e, 0x04); /* CP core pre-gain control */ -		cp_write(sd, 0xc3, 0x39); /* CP coast control. Graphics mode */ -		cp_write(sd, 0x40, 0x5c); /* CP core pre-gain control. Graphics mode */ -		break; +		adv7604_write_reg_seq(sd, info->recommended_settings[1]); + +		if (adv7604_has_afe(state)) { +			afe_write(sd, 0x00, 0xff); /* power down ADC */ +			afe_write(sd, 0x01, 0xfe); /* power down Analog Front End */ +			afe_write(sd, 0xc8, 0x40); /* phase control */ +		} -	case ADV7604_MODE_HDMI: -		/* set ADI recommended settings for HDMI: */ -		/* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */ -		hdmi_write(sd, 0x0d, 0x84); /* HDMI filter optimization */ -		hdmi_write(sd, 0x3d, 0x10); /* DDC bus active pull-up control */ -		hdmi_write(sd, 0x3e, 0x39); /* TMDS PLL optimization */ -		hdmi_write(sd, 0x4e, 0x3b); /* TMDS PLL optimization */ -		hdmi_write(sd, 0x57, 0xb6); /* TMDS PLL optimization */ -		hdmi_write(sd, 0x58, 0x03); /* TMDS PLL optimization */ -		hdmi_write(sd, 0x8d, 0x18); /* equaliser */ -		hdmi_write(sd, 0x8e, 0x34); /* equaliser */ -		hdmi_write(sd, 0x93, 0x8b); /* equaliser */ -		hdmi_write(sd, 0x94, 0x2d); /* equaliser */ -		hdmi_write(sd, 0x96, 0x01); /* enable automatic EQ changing */ - -		afe_write(sd, 0x00, 0xff); /* power down ADC */ -		afe_write(sd, 0x01, 0xfe); /* power down Analog Front End */ -		afe_write(sd, 0xc8, 0x40); /* phase control */ - -		/* reset ADI recommended settings for digitizer */ -		/* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */ -		afe_write(sd, 0x12, 0xfb); /* ADC noise shaping filter controls */ -		afe_write(sd, 0x0c, 0x0d); /* CP core gain controls */  		cp_write(sd, 0x3e, 0x00); /* CP core pre-gain control */  		cp_write(sd, 0xc3, 0x39); /* CP coast control. Graphics mode */  		cp_write(sd, 0x40, 0x80); /* CP core pre-gain control. Graphics mode */ - -		break; -	default: -		v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", -				__func__, state->mode); -		break; +	} else { +		v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n", +				__func__, state->selected_input);  	}  } @@ -1475,9 +1786,16 @@ static int adv7604_s_routing(struct v4l2_subdev *sd,  {  	struct adv7604_state *state = to_state(sd); -	v4l2_dbg(2, debug, sd, "%s: input %d", __func__, input); +	v4l2_dbg(2, debug, sd, "%s: input %d, selected input %d", +			__func__, input, state->selected_input); + +	if (input == state->selected_input) +		return 0; + +	if (input > state->info->max_port) +		return -EINVAL; -	state->mode = input; +	state->selected_input = input;  	disable_input(sd); @@ -1488,64 +1806,182 @@ static int adv7604_s_routing(struct v4l2_subdev *sd,  	return 0;  } -static int adv7604_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index, -			     enum v4l2_mbus_pixelcode *code) +static int adv7604_enum_mbus_code(struct v4l2_subdev *sd, +				  struct v4l2_subdev_fh *fh, +				  struct v4l2_subdev_mbus_code_enum *code)  { -	if (index) +	struct adv7604_state *state = to_state(sd); + +	if (code->index >= state->info->nformats)  		return -EINVAL; -	/* Good enough for now */ -	*code = V4L2_MBUS_FMT_FIXED; + +	code->code = state->info->formats[code->index].code; +  	return 0;  } -static int adv7604_g_mbus_fmt(struct v4l2_subdev *sd, -		struct v4l2_mbus_framefmt *fmt) +static void adv7604_fill_format(struct adv7604_state *state, +				struct v4l2_mbus_framefmt *format)  { -	struct adv7604_state *state = to_state(sd); +	memset(format, 0, sizeof(*format)); -	fmt->width = state->timings.bt.width; -	fmt->height = state->timings.bt.height; -	fmt->code = V4L2_MBUS_FMT_FIXED; -	fmt->field = V4L2_FIELD_NONE; -	if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) { -		fmt->colorspace = (state->timings.bt.height <= 576) ? +	format->width = state->timings.bt.width; +	format->height = state->timings.bt.height; +	format->field = V4L2_FIELD_NONE; + +	if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) +		format->colorspace = (state->timings.bt.height <= 576) ?  			V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709; +} + +/* + * Compute the op_ch_sel value required to obtain on the bus the component order + * corresponding to the selected format taking into account bus reordering + * applied by the board at the output of the device. + * + * The following table gives the op_ch_value from the format component order + * (expressed as op_ch_sel value in column) and the bus reordering (expressed as + * adv7604_bus_order value in row). + * + *           |	GBR(0)	GRB(1)	BGR(2)	RGB(3)	BRG(4)	RBG(5) + * ----------+------------------------------------------------- + * RGB (NOP) |	GBR	GRB	BGR	RGB	BRG	RBG + * GRB (1-2) |	BGR	RGB	GBR	GRB	RBG	BRG + * RBG (2-3) |	GRB	GBR	BRG	RBG	BGR	RGB + * BGR (1-3) |	RBG	BRG	RGB	BGR	GRB	GBR + * BRG (ROR) |	BRG	RBG	GRB	GBR	RGB	BGR + * GBR (ROL) |	RGB	BGR	RBG	BRG	GBR	GRB + */ +static unsigned int adv7604_op_ch_sel(struct adv7604_state *state) +{ +#define _SEL(a,b,c,d,e,f)	{ \ +	ADV7604_OP_CH_SEL_##a, ADV7604_OP_CH_SEL_##b, ADV7604_OP_CH_SEL_##c, \ +	ADV7604_OP_CH_SEL_##d, ADV7604_OP_CH_SEL_##e, ADV7604_OP_CH_SEL_##f } +#define _BUS(x)			[ADV7604_BUS_ORDER_##x] + +	static const unsigned int op_ch_sel[6][6] = { +		_BUS(RGB) /* NOP */ = _SEL(GBR, GRB, BGR, RGB, BRG, RBG), +		_BUS(GRB) /* 1-2 */ = _SEL(BGR, RGB, GBR, GRB, RBG, BRG), +		_BUS(RBG) /* 2-3 */ = _SEL(GRB, GBR, BRG, RBG, BGR, RGB), +		_BUS(BGR) /* 1-3 */ = _SEL(RBG, BRG, RGB, BGR, GRB, GBR), +		_BUS(BRG) /* ROR */ = _SEL(BRG, RBG, GRB, GBR, RGB, BGR), +		_BUS(GBR) /* ROL */ = _SEL(RGB, BGR, RBG, BRG, GBR, GRB), +	}; + +	return op_ch_sel[state->pdata.bus_order][state->format->op_ch_sel >> 5]; +} + +static void adv7604_setup_format(struct adv7604_state *state) +{ +	struct v4l2_subdev *sd = &state->sd; + +	io_write_clr_set(sd, 0x02, 0x02, +			state->format->rgb_out ? ADV7604_RGB_OUT : 0); +	io_write(sd, 0x03, state->format->op_format_sel | +		 state->pdata.op_format_mode_sel); +	io_write_clr_set(sd, 0x04, 0xe0, adv7604_op_ch_sel(state)); +	io_write_clr_set(sd, 0x05, 0x01, +			state->format->swap_cb_cr ? ADV7604_OP_SWAP_CB_CR : 0); +} + +static int adv7604_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +			      struct v4l2_subdev_format *format) +{ +	struct adv7604_state *state = to_state(sd); + +	if (format->pad != state->source_pad) +		return -EINVAL; + +	adv7604_fill_format(state, &format->format); + +	if (format->which == V4L2_SUBDEV_FORMAT_TRY) { +		struct v4l2_mbus_framefmt *fmt; + +		fmt = v4l2_subdev_get_try_format(fh, format->pad); +		format->format.code = fmt->code; +	} else { +		format->format.code = state->format->code; +	} + +	return 0; +} + +static int adv7604_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +			      struct v4l2_subdev_format *format) +{ +	struct adv7604_state *state = to_state(sd); +	const struct adv7604_format_info *info; + +	if (format->pad != state->source_pad) +		return -EINVAL; + +	info = adv7604_format_info(state, format->format.code); +	if (info == NULL) +		info = adv7604_format_info(state, V4L2_MBUS_FMT_YUYV8_2X8); + +	adv7604_fill_format(state, &format->format); +	format->format.code = info->code; + +	if (format->which == V4L2_SUBDEV_FORMAT_TRY) { +		struct v4l2_mbus_framefmt *fmt; + +		fmt = v4l2_subdev_get_try_format(fh, format->pad); +		fmt->code = format->format.code; +	} else { +		state->format = info; +		adv7604_setup_format(state);  	} +  	return 0;  }  static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled)  {  	struct adv7604_state *state = to_state(sd); -	u8 fmt_change, fmt_change_digital, tx_5v; -	u32 input_status; +	const struct adv7604_chip_info *info = state->info; +	const u8 irq_reg_0x43 = io_read(sd, 0x43); +	const u8 irq_reg_0x6b = io_read(sd, 0x6b); +	const u8 irq_reg_0x70 = io_read(sd, 0x70); +	u8 fmt_change_digital; +	u8 fmt_change; +	u8 tx_5v; + +	if (irq_reg_0x43) +		io_write(sd, 0x44, irq_reg_0x43); +	if (irq_reg_0x70) +		io_write(sd, 0x71, irq_reg_0x70); +	if (irq_reg_0x6b) +		io_write(sd, 0x6c, irq_reg_0x6b); + +	v4l2_dbg(2, debug, sd, "%s: ", __func__);  	/* format change */ -	fmt_change = io_read(sd, 0x43) & 0x98; -	if (fmt_change) -		io_write(sd, 0x44, fmt_change); -	fmt_change_digital = DIGITAL_INPUT ? (io_read(sd, 0x6b) & 0xc0) : 0; -	if (fmt_change_digital) -		io_write(sd, 0x6c, fmt_change_digital); +	fmt_change = irq_reg_0x43 & 0x98; +	fmt_change_digital = is_digital_input(sd) +			   ? irq_reg_0x6b & info->fmt_change_digital_mask +			   : 0; +  	if (fmt_change || fmt_change_digital) {  		v4l2_dbg(1, debug, sd,  			"%s: fmt_change = 0x%x, fmt_change_digital = 0x%x\n",  			__func__, fmt_change, fmt_change_digital); -		adv7604_g_input_status(sd, &input_status); -		if (input_status != state->prev_input_status) { -			v4l2_dbg(1, debug, sd, -				"%s: input_status = 0x%x, prev_input_status = 0x%x\n", -				__func__, input_status, state->prev_input_status); -			state->prev_input_status = input_status; -			v4l2_subdev_notify(sd, ADV7604_FMT_CHANGE, NULL); -		} +		v4l2_subdev_notify(sd, ADV7604_FMT_CHANGE, NULL);  		if (handled)  			*handled = true;  	} +	/* HDMI/DVI mode */ +	if (irq_reg_0x6b & 0x01) { +		v4l2_dbg(1, debug, sd, "%s: irq %s mode\n", __func__, +			(io_read(sd, 0x6a) & 0x01) ? "HDMI" : "DVI"); +		set_rgb_quantization_range(sd); +		if (handled) +			*handled = true; +	} +  	/* tx 5v detect */ -	tx_5v = io_read(sd, 0x70) & 0x10; +	tx_5v = io_read(sd, 0x70) & info->cable_det_mask;  	if (tx_5v) {  		v4l2_dbg(1, debug, sd, "%s: tx_5v: 0x%x\n", __func__, tx_5v);  		io_write(sd, 0x71, tx_5v); @@ -1556,58 +1992,183 @@ static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled)  	return 0;  } -static int adv7604_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid) +static int adv7604_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)  {  	struct adv7604_state *state = to_state(sd); +	u8 *data = NULL; -	if (edid->pad != 0) +	if (edid->pad > ADV7604_PAD_HDMI_PORT_D)  		return -EINVAL;  	if (edid->blocks == 0)  		return -EINVAL; -	if (edid->start_block >= state->edid_blocks) +	if (edid->blocks > 2) +		return -EINVAL; +	if (edid->start_block > 1)  		return -EINVAL; -	if (edid->start_block + edid->blocks > state->edid_blocks) -		edid->blocks = state->edid_blocks - edid->start_block; -	if (!edid->edid) +	if (edid->start_block == 1) +		edid->blocks = 1; + +	if (edid->blocks > state->edid.blocks) +		edid->blocks = state->edid.blocks; + +	switch (edid->pad) { +	case ADV7604_PAD_HDMI_PORT_A: +	case ADV7604_PAD_HDMI_PORT_B: +	case ADV7604_PAD_HDMI_PORT_C: +	case ADV7604_PAD_HDMI_PORT_D: +		if (state->edid.present & (1 << edid->pad)) +			data = state->edid.edid; +		break; +	default:  		return -EINVAL; -	memcpy(edid->edid + edid->start_block * 128, -	       state->edid + edid->start_block * 128, +		break; +	} +	if (!data) +		return -ENODATA; + +	memcpy(edid->edid, +	       data + edid->start_block * 128,  	       edid->blocks * 128);  	return 0;  } -static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid) +static int get_edid_spa_location(const u8 *edid) +{ +	u8 d; + +	if ((edid[0x7e] != 1) || +	    (edid[0x80] != 0x02) || +	    (edid[0x81] != 0x03)) { +		return -1; +	} + +	/* search Vendor Specific Data Block (tag 3) */ +	d = edid[0x82] & 0x7f; +	if (d > 4) { +		int i = 0x84; +		int end = 0x80 + d; + +		do { +			u8 tag = edid[i] >> 5; +			u8 len = edid[i] & 0x1f; + +			if ((tag == 3) && (len >= 5)) +				return i + 4; +			i += len + 1; +		} while (i < end); +	} +	return -1; +} + +static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)  {  	struct adv7604_state *state = to_state(sd); +	const struct adv7604_chip_info *info = state->info; +	int spa_loc;  	int err; +	int i; -	if (edid->pad != 0) +	if (edid->pad > ADV7604_PAD_HDMI_PORT_D)  		return -EINVAL;  	if (edid->start_block != 0)  		return -EINVAL;  	if (edid->blocks == 0) { -		/* Pull down the hotplug pin */ -		v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)0); -		/* Disables I2C access to internal EDID ram from DDC port */ -		rep_write_and_or(sd, 0x77, 0xf0, 0x0); -		state->edid_blocks = 0; +		/* Disable hotplug and I2C access to EDID RAM from DDC port */ +		state->edid.present &= ~(1 << edid->pad); +		adv7604_set_hpd(state, state->edid.present); +		rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, state->edid.present); +  		/* Fall back to a 16:9 aspect ratio */  		state->aspect_ratio.numerator = 16;  		state->aspect_ratio.denominator = 9; + +		if (!state->edid.present) +			state->edid.blocks = 0; + +		v4l2_dbg(2, debug, sd, "%s: clear EDID pad %d, edid.present = 0x%x\n", +				__func__, edid->pad, state->edid.present);  		return 0;  	} -	if (edid->blocks > 2) +	if (edid->blocks > 2) { +		edid->blocks = 2;  		return -E2BIG; -	if (!edid->edid) +	} + +	v4l2_dbg(2, debug, sd, "%s: write EDID pad %d, edid.present = 0x%x\n", +			__func__, edid->pad, state->edid.present); + +	/* Disable hotplug and I2C access to EDID RAM from DDC port */ +	cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); +	adv7604_set_hpd(state, 0); +	rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, 0x00); + +	spa_loc = get_edid_spa_location(edid->edid); +	if (spa_loc < 0) +		spa_loc = 0xc0; /* Default value [REF_02, p. 116] */ + +	switch (edid->pad) { +	case ADV7604_PAD_HDMI_PORT_A: +		state->spa_port_a[0] = edid->edid[spa_loc]; +		state->spa_port_a[1] = edid->edid[spa_loc + 1]; +		break; +	case ADV7604_PAD_HDMI_PORT_B: +		rep_write(sd, 0x70, edid->edid[spa_loc]); +		rep_write(sd, 0x71, edid->edid[spa_loc + 1]); +		break; +	case ADV7604_PAD_HDMI_PORT_C: +		rep_write(sd, 0x72, edid->edid[spa_loc]); +		rep_write(sd, 0x73, edid->edid[spa_loc + 1]); +		break; +	case ADV7604_PAD_HDMI_PORT_D: +		rep_write(sd, 0x74, edid->edid[spa_loc]); +		rep_write(sd, 0x75, edid->edid[spa_loc + 1]); +		break; +	default:  		return -EINVAL; -	memcpy(state->edid, edid->edid, 128 * edid->blocks); -	state->edid_blocks = edid->blocks; +	} + +	if (info->type == ADV7604) { +		rep_write(sd, 0x76, spa_loc & 0xff); +		rep_write_clr_set(sd, 0x77, 0x40, (spa_loc & 0x100) >> 2); +	} else { +		/* FIXME: Where is the SPA location LSB register ? */ +		rep_write_clr_set(sd, 0x71, 0x01, (spa_loc & 0x100) >> 8); +	} + +	edid->edid[spa_loc] = state->spa_port_a[0]; +	edid->edid[spa_loc + 1] = state->spa_port_a[1]; + +	memcpy(state->edid.edid, edid->edid, 128 * edid->blocks); +	state->edid.blocks = edid->blocks;  	state->aspect_ratio = v4l2_calc_aspect_ratio(edid->edid[0x15],  			edid->edid[0x16]); -	err = edid_write_block(sd, 128 * edid->blocks, state->edid); -	if (err < 0) -		v4l2_err(sd, "error %d writing edid\n", err); -	return err; +	state->edid.present |= 1 << edid->pad; + +	err = edid_write_block(sd, 128 * edid->blocks, state->edid.edid); +	if (err < 0) { +		v4l2_err(sd, "error %d writing edid pad %d\n", err, edid->pad); +		return err; +	} + +	/* adv7604 calculates the checksums and enables I2C access to internal +	   EDID RAM from DDC port. */ +	rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, state->edid.present); + +	for (i = 0; i < 1000; i++) { +		if (rep_read(sd, info->edid_status_reg) & state->edid.present) +			break; +		mdelay(1); +	} +	if (i == 1000) { +		v4l2_err(sd, "error enabling edid (0x%x)\n", state->edid.present); +		return -EIO; +	} + + +	/* enable hotplug after 100 ms */ +	queue_delayed_work(state->work_queues, +			&state->delayed_work_enable_hotplug, HZ / 10); +	return 0;  }  /*********** avi info frame CEA-861-E **************/ @@ -1657,30 +2218,33 @@ static void print_avi_infoframe(struct v4l2_subdev *sd)  static int adv7604_log_status(struct v4l2_subdev *sd)  {  	struct adv7604_state *state = to_state(sd); +	const struct adv7604_chip_info *info = state->info;  	struct v4l2_dv_timings timings;  	struct stdi_readback stdi;  	u8 reg_io_0x02 = io_read(sd, 0x02); +	u8 edid_enabled; +	u8 cable_det; -	char *csc_coeff_sel_rb[16] = { +	static const char * const csc_coeff_sel_rb[16] = {  		"bypassed", "YPbPr601 -> RGB", "reserved", "YPbPr709 -> RGB",  		"reserved", "RGB -> YPbPr601", "reserved", "RGB -> YPbPr709",  		"reserved", "YPbPr709 -> YPbPr601", "YPbPr601 -> YPbPr709",  		"reserved", "reserved", "reserved", "reserved", "manual"  	}; -	char *input_color_space_txt[16] = { +	static const char * const input_color_space_txt[16] = {  		"RGB limited range (16-235)", "RGB full range (0-255)",  		"YCbCr Bt.601 (16-235)", "YCbCr Bt.709 (16-235)", -		"XvYCC Bt.601", "XvYCC Bt.709", +		"xvYCC Bt.601", "xvYCC Bt.709",  		"YCbCr Bt.601 (0-255)", "YCbCr Bt.709 (0-255)",  		"invalid", "invalid", "invalid", "invalid", "invalid",  		"invalid", "invalid", "automatic"  	}; -	char *rgb_quantization_range_txt[] = { +	static const char * const rgb_quantization_range_txt[] = {  		"Automatic",  		"RGB limited range (16-235)",  		"RGB full range (0-255)",  	}; -	char *deep_color_mode_txt[4] = { +	static const char * const deep_color_mode_txt[4] = {  		"8-bits per channel",  		"10-bits per channel",  		"12-bits per channel", @@ -1689,16 +2253,22 @@ static int adv7604_log_status(struct v4l2_subdev *sd)  	v4l2_info(sd, "-----Chip status-----\n");  	v4l2_info(sd, "Chip power: %s\n", no_power(sd) ? "off" : "on"); -	v4l2_info(sd, "Connector type: %s\n", state->connector_hdmi ? -			"HDMI" : (DIGITAL_INPUT ? "DVI-D" : "DVI-A")); -	v4l2_info(sd, "EDID: %s\n", ((rep_read(sd, 0x7d) & 0x01) && -			(rep_read(sd, 0x77) & 0x01)) ? "enabled" : "disabled "); +	edid_enabled = rep_read(sd, info->edid_status_reg); +	v4l2_info(sd, "EDID enabled port A: %s, B: %s, C: %s, D: %s\n", +			((edid_enabled & 0x01) ? "Yes" : "No"), +			((edid_enabled & 0x02) ? "Yes" : "No"), +			((edid_enabled & 0x04) ? "Yes" : "No"), +			((edid_enabled & 0x08) ? "Yes" : "No"));  	v4l2_info(sd, "CEC: %s\n", !!(cec_read(sd, 0x2a) & 0x01) ?  			"enabled" : "disabled");  	v4l2_info(sd, "-----Signal status-----\n"); -	v4l2_info(sd, "Cable detected (+5V power): %s\n", -			(io_read(sd, 0x6f) & 0x10) ? "true" : "false"); +	cable_det = info->read_cable_det(sd); +	v4l2_info(sd, "Cable detected (+5V power) port A: %s, B: %s, C: %s, D: %s\n", +			((cable_det & 0x01) ? "Yes" : "No"), +			((cable_det & 0x02) ? "Yes" : "No"), +			((cable_det & 0x04) ? "Yes" : "No"), +			((cable_det & 0x08) ? "Yes" : "No"));  	v4l2_info(sd, "TMDS signal detected: %s\n",  			no_signal_tmds(sd) ? "false" : "true");  	v4l2_info(sd, "TMDS signal locked: %s\n", @@ -1744,11 +2314,14 @@ static int adv7604_log_status(struct v4l2_subdev *sd)  	v4l2_info(sd, "Color space conversion: %s\n",  			csc_coeff_sel_rb[cp_read(sd, 0xfc) >> 4]); -	if (!DIGITAL_INPUT) +	if (!is_digital_input(sd))  		return 0;  	v4l2_info(sd, "-----%s status-----\n", is_hdmi(sd) ? "HDMI" : "DVI-D"); -	v4l2_info(sd, "HDCP encrypted content: %s\n", (hdmi_read(sd, 0x05) & 0x40) ? "true" : "false"); +	v4l2_info(sd, "Digital video port selected: %c\n", +			(hdmi_read(sd, 0x00) & 0x03) + 'A'); +	v4l2_info(sd, "HDCP encrypted content: %s\n", +			(hdmi_read(sd, 0x05) & 0x40) ? "true" : "false");  	v4l2_info(sd, "HDCP keys read: %s%s\n",  			(hdmi_read(sd, 0x04) & 0x20) ? "yes" : "no",  			(hdmi_read(sd, 0x04) & 0x10) ? "ERROR" : ""); @@ -1789,13 +2362,6 @@ static const struct v4l2_ctrl_ops adv7604_ctrl_ops = {  static const struct v4l2_subdev_core_ops adv7604_core_ops = {  	.log_status = adv7604_log_status, -	.g_ext_ctrls = v4l2_subdev_g_ext_ctrls, -	.try_ext_ctrls = v4l2_subdev_try_ext_ctrls, -	.s_ext_ctrls = v4l2_subdev_s_ext_ctrls, -	.g_ctrl = v4l2_subdev_g_ctrl, -	.s_ctrl = v4l2_subdev_s_ctrl, -	.queryctrl = v4l2_subdev_queryctrl, -	.querymenu = v4l2_subdev_querymenu,  	.interrupt_service_routine = adv7604_isr,  #ifdef CONFIG_VIDEO_ADV_DEBUG  	.g_register = adv7604_g_register, @@ -1809,17 +2375,16 @@ static const struct v4l2_subdev_video_ops adv7604_video_ops = {  	.s_dv_timings = adv7604_s_dv_timings,  	.g_dv_timings = adv7604_g_dv_timings,  	.query_dv_timings = adv7604_query_dv_timings, -	.enum_dv_timings = adv7604_enum_dv_timings, -	.dv_timings_cap = adv7604_dv_timings_cap, -	.enum_mbus_fmt = adv7604_enum_mbus_fmt, -	.g_mbus_fmt = adv7604_g_mbus_fmt, -	.try_mbus_fmt = adv7604_g_mbus_fmt, -	.s_mbus_fmt = adv7604_g_mbus_fmt,  };  static const struct v4l2_subdev_pad_ops adv7604_pad_ops = { +	.enum_mbus_code = adv7604_enum_mbus_code, +	.get_fmt = adv7604_get_format, +	.set_fmt = adv7604_set_format,  	.get_edid = adv7604_get_edid,  	.set_edid = adv7604_set_edid, +	.dv_timings_cap = adv7604_dv_timings_cap, +	.enum_dv_timings = adv7604_enum_dv_timings,  };  static const struct v4l2_subdev_ops adv7604_ops = { @@ -1868,6 +2433,7 @@ static const struct v4l2_ctrl_config adv7604_ctrl_free_run_color = {  static int adv7604_core_init(struct v4l2_subdev *sd)  {  	struct adv7604_state *state = to_state(sd); +	const struct adv7604_chip_info *info = state->info;  	struct adv7604_platform_data *pdata = &state->pdata;  	hdmi_write(sd, 0x48, @@ -1876,28 +2442,39 @@ static int adv7604_core_init(struct v4l2_subdev *sd)  	disable_input(sd); +	if (pdata->default_input >= 0 && +	    pdata->default_input < state->source_pad) { +		state->selected_input = pdata->default_input; +		select_input(sd); +		enable_input(sd); +	} +  	/* power */  	io_write(sd, 0x0c, 0x42);   /* Power up part and power down VDP */  	io_write(sd, 0x0b, 0x44);   /* Power down ESDP block */  	cp_write(sd, 0xcf, 0x01);   /* Power down macrovision */  	/* video format */ -	io_write_and_or(sd, 0x02, 0xf0, +	io_write_clr_set(sd, 0x02, 0x0f,  			pdata->alt_gamma << 3 |  			pdata->op_656_range << 2 | -			pdata->rgb_out << 1 |  			pdata->alt_data_sat << 0); -	io_write(sd, 0x03, pdata->op_format_sel); -	io_write_and_or(sd, 0x04, 0x1f, pdata->op_ch_sel << 5); -	io_write_and_or(sd, 0x05, 0xf0, pdata->blank_data << 3 | -					pdata->insert_av_codes << 2 | -					pdata->replicate_av_codes << 1 | -					pdata->invert_cbcr << 0); +	io_write_clr_set(sd, 0x05, 0x0e, pdata->blank_data << 3 | +			pdata->insert_av_codes << 2 | +			pdata->replicate_av_codes << 1); +	adv7604_setup_format(state); -	/* TODO from platform data */  	cp_write(sd, 0x69, 0x30);   /* Enable CP CSC */ -	io_write(sd, 0x06, 0xa6);   /* positive VS and HS */ -	io_write(sd, 0x14, 0x7f);   /* Drive strength adjusted to max */ + +	/* VS, HS polarities */ +	io_write(sd, 0x06, 0xa0 | pdata->inv_vs_pol << 2 | +		 pdata->inv_hs_pol << 1 | pdata->inv_llc_pol); + +	/* Adjust drive strength */ +	io_write(sd, 0x14, 0x40 | pdata->dr_str_data << 4 | +				pdata->dr_str_clk << 2 | +				pdata->dr_str_sync); +  	cp_write(sd, 0xba, (pdata->hdmi_free_run_mode << 1) | 0x01); /* HDMI free run */  	cp_write(sd, 0xf3, 0xdc); /* Low threshold to enter/exit free run mode */  	cp_write(sd, 0xf9, 0x23); /*  STDI ch. 1 - LCVS change threshold - @@ -1907,48 +2484,47 @@ static int adv7604_core_init(struct v4l2_subdev *sd)  	cp_write(sd, 0xc9, 0x2d); /* use prim_mode and vid_std as free run resolution  				     for digital formats */ +	/* HDMI audio */ +	hdmi_write_clr_set(sd, 0x15, 0x03, 0x03); /* Mute on FIFO over-/underflow [REF_01, c. 1.2.18] */ +	hdmi_write_clr_set(sd, 0x1a, 0x0e, 0x08); /* Wait 1 s before unmute */ +	hdmi_write_clr_set(sd, 0x68, 0x06, 0x06); /* FIFO reset on over-/underflow [REF_01, c. 1.2.19] */ +  	/* TODO from platform data */  	afe_write(sd, 0xb5, 0x01);  /* Setting MCLK to 256Fs */ -	afe_write(sd, 0x02, pdata->ain_sel); /* Select analog input muxing mode */ -	io_write_and_or(sd, 0x30, ~(1 << 4), pdata->output_bus_lsb_to_msb << 4); +	if (adv7604_has_afe(state)) { +		afe_write(sd, 0x02, pdata->ain_sel); /* Select analog input muxing mode */ +		io_write_clr_set(sd, 0x30, 1 << 4, pdata->output_bus_lsb_to_msb << 4); +	}  	/* interrupts */ -	io_write(sd, 0x40, 0xc2); /* Configure INT1 */ -	io_write(sd, 0x41, 0xd7); /* STDI irq for any change, disable INT2 */ +	io_write(sd, 0x40, 0xc0 | pdata->int1_config); /* Configure INT1 */  	io_write(sd, 0x46, 0x98); /* Enable SSPD, STDI and CP unlocked interrupts */ -	io_write(sd, 0x6e, 0xc0); /* Enable V_LOCKED and DE_REGEN_LCK interrupts */ -	io_write(sd, 0x73, 0x10); /* Enable CABLE_DET_A_ST (+5v) interrupt */ +	io_write(sd, 0x6e, info->fmt_change_digital_mask); /* Enable V_LOCKED and DE_REGEN_LCK interrupts */ +	io_write(sd, 0x73, info->cable_det_mask); /* Enable cable detection (+5v) interrupts */ +	info->setup_irqs(sd);  	return v4l2_ctrl_handler_setup(sd->ctrl_handler);  } +static void adv7604_setup_irqs(struct v4l2_subdev *sd) +{ +	io_write(sd, 0x41, 0xd7); /* STDI irq for any change, disable INT2 */ +} + +static void adv7611_setup_irqs(struct v4l2_subdev *sd) +{ +	io_write(sd, 0x41, 0xd0); /* STDI irq for any change, disable INT2 */ +} +  static void adv7604_unregister_clients(struct adv7604_state *state)  { -	if (state->i2c_avlink) -		i2c_unregister_device(state->i2c_avlink); -	if (state->i2c_cec) -		i2c_unregister_device(state->i2c_cec); -	if (state->i2c_infoframe) -		i2c_unregister_device(state->i2c_infoframe); -	if (state->i2c_esdp) -		i2c_unregister_device(state->i2c_esdp); -	if (state->i2c_dpp) -		i2c_unregister_device(state->i2c_dpp); -	if (state->i2c_afe) -		i2c_unregister_device(state->i2c_afe); -	if (state->i2c_repeater) -		i2c_unregister_device(state->i2c_repeater); -	if (state->i2c_edid) -		i2c_unregister_device(state->i2c_edid); -	if (state->i2c_hdmi) -		i2c_unregister_device(state->i2c_hdmi); -	if (state->i2c_test) -		i2c_unregister_device(state->i2c_test); -	if (state->i2c_cp) -		i2c_unregister_device(state->i2c_cp); -	if (state->i2c_vdp) -		i2c_unregister_device(state->i2c_vdp); +	unsigned int i; + +	for (i = 1; i < ARRAY_SIZE(state->i2c_clients); ++i) { +		if (state->i2c_clients[i]) +			i2c_unregister_device(state->i2c_clients[i]); +	}  }  static struct i2c_client *adv7604_dummy_client(struct v4l2_subdev *sd, @@ -1961,13 +2537,219 @@ static struct i2c_client *adv7604_dummy_client(struct v4l2_subdev *sd,  	return i2c_new_dummy(client->adapter, io_read(sd, io_reg) >> 1);  } +static const struct adv7604_reg_seq adv7604_recommended_settings_afe[] = { +	/* reset ADI recommended settings for HDMI: */ +	/* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */ +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x0d), 0x04 }, /* HDMI filter optimization */ +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x0d), 0x04 }, /* HDMI filter optimization */ +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x3d), 0x00 }, /* DDC bus active pull-up control */ +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x3e), 0x74 }, /* TMDS PLL optimization */ +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x4e), 0x3b }, /* TMDS PLL optimization */ +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x57), 0x74 }, /* TMDS PLL optimization */ +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x58), 0x63 }, /* TMDS PLL optimization */ +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x8d), 0x18 }, /* equaliser */ +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x8e), 0x34 }, /* equaliser */ +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x93), 0x88 }, /* equaliser */ +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x94), 0x2e }, /* equaliser */ +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x96), 0x00 }, /* enable automatic EQ changing */ + +	/* set ADI recommended settings for digitizer */ +	/* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */ +	{ ADV7604_REG(ADV7604_PAGE_AFE, 0x12), 0x7b }, /* ADC noise shaping filter controls */ +	{ ADV7604_REG(ADV7604_PAGE_AFE, 0x0c), 0x1f }, /* CP core gain controls */ +	{ ADV7604_REG(ADV7604_PAGE_CP, 0x3e), 0x04 }, /* CP core pre-gain control */ +	{ ADV7604_REG(ADV7604_PAGE_CP, 0xc3), 0x39 }, /* CP coast control. Graphics mode */ +	{ ADV7604_REG(ADV7604_PAGE_CP, 0x40), 0x5c }, /* CP core pre-gain control. Graphics mode */ + +	{ ADV7604_REG_SEQ_TERM, 0 }, +}; + +static const struct adv7604_reg_seq adv7604_recommended_settings_hdmi[] = { +	/* set ADI recommended settings for HDMI: */ +	/* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */ +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x0d), 0x84 }, /* HDMI filter optimization */ +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x3d), 0x10 }, /* DDC bus active pull-up control */ +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x3e), 0x39 }, /* TMDS PLL optimization */ +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x4e), 0x3b }, /* TMDS PLL optimization */ +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x57), 0xb6 }, /* TMDS PLL optimization */ +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x58), 0x03 }, /* TMDS PLL optimization */ +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x8d), 0x18 }, /* equaliser */ +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x8e), 0x34 }, /* equaliser */ +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x93), 0x8b }, /* equaliser */ +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x94), 0x2d }, /* equaliser */ +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x96), 0x01 }, /* enable automatic EQ changing */ + +	/* reset ADI recommended settings for digitizer */ +	/* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */ +	{ ADV7604_REG(ADV7604_PAGE_AFE, 0x12), 0xfb }, /* ADC noise shaping filter controls */ +	{ ADV7604_REG(ADV7604_PAGE_AFE, 0x0c), 0x0d }, /* CP core gain controls */ + +	{ ADV7604_REG_SEQ_TERM, 0 }, +}; + +static const struct adv7604_reg_seq adv7611_recommended_settings_hdmi[] = { +	{ ADV7604_REG(ADV7604_PAGE_CP, 0x6c), 0x00 }, +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x6f), 0x0c }, +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x87), 0x70 }, +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x57), 0xda }, +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x58), 0x01 }, +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x03), 0x98 }, +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x4c), 0x44 }, +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x8d), 0x04 }, +	{ ADV7604_REG(ADV7604_PAGE_HDMI, 0x8e), 0x1e }, + +	{ ADV7604_REG_SEQ_TERM, 0 }, +}; + +static const struct adv7604_chip_info adv7604_chip_info[] = { +	[ADV7604] = { +		.type = ADV7604, +		.has_afe = true, +		.max_port = ADV7604_PAD_VGA_COMP, +		.num_dv_ports = 4, +		.edid_enable_reg = 0x77, +		.edid_status_reg = 0x7d, +		.lcf_reg = 0xb3, +		.tdms_lock_mask = 0xe0, +		.cable_det_mask = 0x1e, +		.fmt_change_digital_mask = 0xc1, +		.formats = adv7604_formats, +		.nformats = ARRAY_SIZE(adv7604_formats), +		.set_termination = adv7604_set_termination, +		.setup_irqs = adv7604_setup_irqs, +		.read_hdmi_pixelclock = adv7604_read_hdmi_pixelclock, +		.read_cable_det = adv7604_read_cable_det, +		.recommended_settings = { +		    [0] = adv7604_recommended_settings_afe, +		    [1] = adv7604_recommended_settings_hdmi, +		}, +		.num_recommended_settings = { +		    [0] = ARRAY_SIZE(adv7604_recommended_settings_afe), +		    [1] = ARRAY_SIZE(adv7604_recommended_settings_hdmi), +		}, +		.page_mask = BIT(ADV7604_PAGE_IO) | BIT(ADV7604_PAGE_AVLINK) | +			BIT(ADV7604_PAGE_CEC) | BIT(ADV7604_PAGE_INFOFRAME) | +			BIT(ADV7604_PAGE_ESDP) | BIT(ADV7604_PAGE_DPP) | +			BIT(ADV7604_PAGE_AFE) | BIT(ADV7604_PAGE_REP) | +			BIT(ADV7604_PAGE_EDID) | BIT(ADV7604_PAGE_HDMI) | +			BIT(ADV7604_PAGE_TEST) | BIT(ADV7604_PAGE_CP) | +			BIT(ADV7604_PAGE_VDP), +	}, +	[ADV7611] = { +		.type = ADV7611, +		.has_afe = false, +		.max_port = ADV7604_PAD_HDMI_PORT_A, +		.num_dv_ports = 1, +		.edid_enable_reg = 0x74, +		.edid_status_reg = 0x76, +		.lcf_reg = 0xa3, +		.tdms_lock_mask = 0x43, +		.cable_det_mask = 0x01, +		.fmt_change_digital_mask = 0x03, +		.formats = adv7611_formats, +		.nformats = ARRAY_SIZE(adv7611_formats), +		.set_termination = adv7611_set_termination, +		.setup_irqs = adv7611_setup_irqs, +		.read_hdmi_pixelclock = adv7611_read_hdmi_pixelclock, +		.read_cable_det = adv7611_read_cable_det, +		.recommended_settings = { +		    [1] = adv7611_recommended_settings_hdmi, +		}, +		.num_recommended_settings = { +		    [1] = ARRAY_SIZE(adv7611_recommended_settings_hdmi), +		}, +		.page_mask = BIT(ADV7604_PAGE_IO) | BIT(ADV7604_PAGE_CEC) | +			BIT(ADV7604_PAGE_INFOFRAME) | BIT(ADV7604_PAGE_AFE) | +			BIT(ADV7604_PAGE_REP) |  BIT(ADV7604_PAGE_EDID) | +			BIT(ADV7604_PAGE_HDMI) | BIT(ADV7604_PAGE_CP), +	}, +}; + +static struct i2c_device_id adv7604_i2c_id[] = { +	{ "adv7604", (kernel_ulong_t)&adv7604_chip_info[ADV7604] }, +	{ "adv7611", (kernel_ulong_t)&adv7604_chip_info[ADV7611] }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, adv7604_i2c_id); + +static struct of_device_id adv7604_of_id[] __maybe_unused = { +	{ .compatible = "adi,adv7611", .data = &adv7604_chip_info[ADV7611] }, +	{ } +}; +MODULE_DEVICE_TABLE(of, adv7604_of_id); + +static int adv7604_parse_dt(struct adv7604_state *state) +{ +	struct v4l2_of_endpoint bus_cfg; +	struct device_node *endpoint; +	struct device_node *np; +	unsigned int flags; + +	np = state->i2c_clients[ADV7604_PAGE_IO]->dev.of_node; + +	/* Parse the endpoint. */ +	endpoint = of_graph_get_next_endpoint(np, NULL); +	if (!endpoint) +		return -EINVAL; + +	v4l2_of_parse_endpoint(endpoint, &bus_cfg); +	of_node_put(endpoint); + +	flags = bus_cfg.bus.parallel.flags; + +	if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) +		state->pdata.inv_hs_pol = 1; + +	if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) +		state->pdata.inv_vs_pol = 1; + +	if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING) +		state->pdata.inv_llc_pol = 1; + +	if (bus_cfg.bus_type == V4L2_MBUS_BT656) { +		state->pdata.insert_av_codes = 1; +		state->pdata.op_656_range = 1; +	} + +	/* Disable the interrupt for now as no DT-based board uses it. */ +	state->pdata.int1_config = ADV7604_INT1_CONFIG_DISABLED; + +	/* Use the default I2C addresses. */ +	state->pdata.i2c_addresses[ADV7604_PAGE_AVLINK] = 0x42; +	state->pdata.i2c_addresses[ADV7604_PAGE_CEC] = 0x40; +	state->pdata.i2c_addresses[ADV7604_PAGE_INFOFRAME] = 0x3e; +	state->pdata.i2c_addresses[ADV7604_PAGE_ESDP] = 0x38; +	state->pdata.i2c_addresses[ADV7604_PAGE_DPP] = 0x3c; +	state->pdata.i2c_addresses[ADV7604_PAGE_AFE] = 0x26; +	state->pdata.i2c_addresses[ADV7604_PAGE_REP] = 0x32; +	state->pdata.i2c_addresses[ADV7604_PAGE_EDID] = 0x36; +	state->pdata.i2c_addresses[ADV7604_PAGE_HDMI] = 0x34; +	state->pdata.i2c_addresses[ADV7604_PAGE_TEST] = 0x30; +	state->pdata.i2c_addresses[ADV7604_PAGE_CP] = 0x22; +	state->pdata.i2c_addresses[ADV7604_PAGE_VDP] = 0x24; + +	/* Hardcode the remaining platform data fields. */ +	state->pdata.disable_pwrdnb = 0; +	state->pdata.disable_cable_det_rst = 0; +	state->pdata.default_input = -1; +	state->pdata.blank_data = 1; +	state->pdata.alt_data_sat = 1; +	state->pdata.op_format_mode_sel = ADV7604_OP_FORMAT_MODE0; +	state->pdata.bus_order = ADV7604_BUS_ORDER_RGB; + +	return 0; +} +  static int adv7604_probe(struct i2c_client *client,  			 const struct i2c_device_id *id)  { +	static const struct v4l2_dv_timings cea640x480 = +		V4L2_DV_BT_CEA_640X480P59_94;  	struct adv7604_state *state; -	struct adv7604_platform_data *pdata = client->dev.platform_data;  	struct v4l2_ctrl_handler *hdl;  	struct v4l2_subdev *sd; +	unsigned int i; +	u16 val;  	int err;  	/* Check if the adapter supports the needed features */ @@ -1982,32 +2764,80 @@ static int adv7604_probe(struct i2c_client *client,  		return -ENOMEM;  	} +	state->i2c_clients[ADV7604_PAGE_IO] = client; +  	/* initialize variables */  	state->restart_stdi_once = true; -	state->prev_input_status = ~0; +	state->selected_input = ~0; + +	if (IS_ENABLED(CONFIG_OF) && client->dev.of_node) { +		const struct of_device_id *oid; + +		oid = of_match_node(adv7604_of_id, client->dev.of_node); +		state->info = oid->data; + +		err = adv7604_parse_dt(state); +		if (err < 0) { +			v4l_err(client, "DT parsing error\n"); +			return err; +		} +	} else if (client->dev.platform_data) { +		struct adv7604_platform_data *pdata = client->dev.platform_data; -	/* platform data */ -	if (!pdata) { +		state->info = (const struct adv7604_chip_info *)id->driver_data; +		state->pdata = *pdata; +	} else {  		v4l_err(client, "No platform data!\n");  		return -ENODEV;  	} -	memcpy(&state->pdata, pdata, sizeof(state->pdata)); + +	/* Request GPIOs. */ +	for (i = 0; i < state->info->num_dv_ports; ++i) { +		state->hpd_gpio[i] = +			devm_gpiod_get_index(&client->dev, "hpd", i); +		if (IS_ERR(state->hpd_gpio[i])) +			continue; + +		gpiod_direction_output(state->hpd_gpio[i], 0); + +		v4l_info(client, "Handling HPD %u GPIO\n", i); +	} + +	state->timings = cea640x480; +	state->format = adv7604_format_info(state, V4L2_MBUS_FMT_YUYV8_2X8);  	sd = &state->sd;  	v4l2_i2c_subdev_init(sd, client, &adv7604_ops); +	snprintf(sd->name, sizeof(sd->name), "%s %d-%04x", +		id->name, i2c_adapter_id(client->adapter), +		client->addr);  	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; -	state->connector_hdmi = pdata->connector_hdmi; -	/* i2c access to adv7604? */ -	if (adv_smbus_read_byte_data_check(client, 0xfb, false) != 0x68) { -		v4l2_info(sd, "not an adv7604 on address 0x%x\n", -				client->addr << 1); -		return -ENODEV; +	/* +	 * Verify that the chip is present. On ADV7604 the RD_INFO register only +	 * identifies the revision, while on ADV7611 it identifies the model as +	 * well. Use the HDMI slave address on ADV7604 and RD_INFO on ADV7611. +	 */ +	if (state->info->type == ADV7604) { +		val = adv_smbus_read_byte_data_check(client, 0xfb, false); +		if (val != 0x68) { +			v4l2_info(sd, "not an adv7604 on address 0x%x\n", +					client->addr << 1); +			return -ENODEV; +		} +	} else { +		val = (adv_smbus_read_byte_data_check(client, 0xea, false) << 8) +		    | (adv_smbus_read_byte_data_check(client, 0xeb, false) << 0); +		if (val != 0x2051) { +			v4l2_info(sd, "not an adv7611 on address 0x%x\n", +					client->addr << 1); +			return -ENODEV; +		}  	}  	/* control handlers */  	hdl = &state->hdl; -	v4l2_ctrl_handler_init(hdl, 9); +	v4l2_ctrl_handler_init(hdl, adv7604_has_afe(state) ? 9 : 8);  	v4l2_ctrl_new_std(hdl, &adv7604_ctrl_ops,  			V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); @@ -2020,15 +2850,17 @@ static int adv7604_probe(struct i2c_client *client,  	/* private controls */  	state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL, -			V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0); +			V4L2_CID_DV_RX_POWER_PRESENT, 0, +			(1 << state->info->num_dv_ports) - 1, 0, 0);  	state->rgb_quantization_range_ctrl =  		v4l2_ctrl_new_std_menu(hdl, &adv7604_ctrl_ops,  			V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL,  			0, V4L2_DV_RGB_RANGE_AUTO);  	/* custom controls */ -	state->analog_sampling_phase_ctrl = -		v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_analog_sampling_phase, NULL); +	if (adv7604_has_afe(state)) +		state->analog_sampling_phase_ctrl = +			v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_analog_sampling_phase, NULL);  	state->free_run_color_manual_ctrl =  		v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_free_run_color_manual, NULL);  	state->free_run_color_ctrl = @@ -2041,7 +2873,8 @@ static int adv7604_probe(struct i2c_client *client,  	}  	state->detect_tx_5v_ctrl->is_private = true;  	state->rgb_quantization_range_ctrl->is_private = true; -	state->analog_sampling_phase_ctrl->is_private = true; +	if (adv7604_has_afe(state)) +		state->analog_sampling_phase_ctrl->is_private = true;  	state->free_run_color_manual_ctrl->is_private = true;  	state->free_run_color_ctrl->is_private = true; @@ -2050,25 +2883,18 @@ static int adv7604_probe(struct i2c_client *client,  		goto err_hdl;  	} -	state->i2c_avlink = adv7604_dummy_client(sd, pdata->i2c_avlink, 0xf3); -	state->i2c_cec = adv7604_dummy_client(sd, pdata->i2c_cec, 0xf4); -	state->i2c_infoframe = adv7604_dummy_client(sd, pdata->i2c_infoframe, 0xf5); -	state->i2c_esdp = adv7604_dummy_client(sd, pdata->i2c_esdp, 0xf6); -	state->i2c_dpp = adv7604_dummy_client(sd, pdata->i2c_dpp, 0xf7); -	state->i2c_afe = adv7604_dummy_client(sd, pdata->i2c_afe, 0xf8); -	state->i2c_repeater = adv7604_dummy_client(sd, pdata->i2c_repeater, 0xf9); -	state->i2c_edid = adv7604_dummy_client(sd, pdata->i2c_edid, 0xfa); -	state->i2c_hdmi = adv7604_dummy_client(sd, pdata->i2c_hdmi, 0xfb); -	state->i2c_test = adv7604_dummy_client(sd, pdata->i2c_test, 0xfc); -	state->i2c_cp = adv7604_dummy_client(sd, pdata->i2c_cp, 0xfd); -	state->i2c_vdp = adv7604_dummy_client(sd, pdata->i2c_vdp, 0xfe); -	if (!state->i2c_avlink || !state->i2c_cec || !state->i2c_infoframe || -	    !state->i2c_esdp || !state->i2c_dpp || !state->i2c_afe || -	    !state->i2c_repeater || !state->i2c_edid || !state->i2c_hdmi || -	    !state->i2c_test || !state->i2c_cp || !state->i2c_vdp) { -		err = -ENOMEM; -		v4l2_err(sd, "failed to create all i2c clients\n"); -		goto err_i2c; +	for (i = 1; i < ADV7604_PAGE_MAX; ++i) { +		if (!(BIT(i) & state->info->page_mask)) +			continue; + +		state->i2c_clients[i] = +			adv7604_dummy_client(sd, state->pdata.i2c_addresses[i], +					     0xf2 + i); +		if (state->i2c_clients[i] == NULL) { +			err = -ENOMEM; +			v4l2_err(sd, "failed to create i2c client %u\n", i); +			goto err_i2c; +		}  	}  	/* work queues */ @@ -2082,8 +2908,14 @@ static int adv7604_probe(struct i2c_client *client,  	INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug,  			adv7604_delayed_work_enable_hotplug); -	state->pad.flags = MEDIA_PAD_FL_SOURCE; -	err = media_entity_init(&sd->entity, 1, &state->pad, 0); +	state->source_pad = state->info->num_dv_ports +			  + (state->info->has_afe ? 2 : 0); +	for (i = 0; i < state->source_pad; ++i) +		state->pads[i].flags = MEDIA_PAD_FL_SINK; +	state->pads[state->source_pad].flags = MEDIA_PAD_FL_SOURCE; + +	err = media_entity_init(&sd->entity, state->source_pad + 1, +				state->pads, 0);  	if (err)  		goto err_work_queues; @@ -2092,6 +2924,11 @@ static int adv7604_probe(struct i2c_client *client,  		goto err_entity;  	v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,  			client->addr << 1, client->adapter->name); + +	err = v4l2_async_register_subdev(sd); +	if (err) +		goto err_entity; +  	return 0;  err_entity: @@ -2115,6 +2952,7 @@ static int adv7604_remove(struct i2c_client *client)  	cancel_delayed_work(&state->delayed_work_enable_hotplug);  	destroy_workqueue(state->work_queues); +	v4l2_async_unregister_subdev(sd);  	v4l2_device_unregister_subdev(sd);  	media_entity_cleanup(&sd->entity);  	adv7604_unregister_clients(to_state(sd)); @@ -2124,20 +2962,15 @@ static int adv7604_remove(struct i2c_client *client)  /* ----------------------------------------------------------------------- */ -static struct i2c_device_id adv7604_id[] = { -	{ "adv7604", 0 }, -	{ } -}; -MODULE_DEVICE_TABLE(i2c, adv7604_id); -  static struct i2c_driver adv7604_driver = {  	.driver = {  		.owner = THIS_MODULE,  		.name = "adv7604", +		.of_match_table = of_match_ptr(adv7604_of_id),  	},  	.probe = adv7604_probe,  	.remove = adv7604_remove, -	.id_table = adv7604_id, +	.id_table = adv7604_i2c_id,  };  module_i2c_driver(adv7604_driver); diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index d1748901337..0d554919cdd 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -20,10 +20,13 @@  /*   * References (c = chapter, p = page): - * REF_01 - Analog devices, ADV7842, Register Settings Recommendations, - *		Revision 2.5, June 2010 - * REF_02 - Analog devices, Register map documentation, Documentation of - *		the register maps, Software manual, Rev. F, June 2010 + * REF_01 - Analog devices, ADV7842, + *		Register Settings Recommendations, Rev. 1.9, April 2011 + * REF_02 - Analog devices, Software User Guide, UG-206, + *		ADV7842 I2C Register Maps, Rev. 0, November 2010 + * REF_03 - Analog devices, Hardware User Guide, UG-214, + *		ADV7842 Fast Switching 2:1 HDMI 1.4 Receiver with 3D-Comb + *		Decoder and Digitizer , Rev. 0, January 2011   */ @@ -61,6 +64,7 @@ MODULE_LICENSE("GPL");  */  struct adv7842_state { +	struct adv7842_platform_data pdata;  	struct v4l2_subdev sd;  	struct media_pad pad;  	struct v4l2_ctrl_handler hdl; @@ -81,7 +85,7 @@ struct adv7842_state {  	bool is_cea_format;  	struct workqueue_struct *work_queues;  	struct delayed_work delayed_work_enable_hotplug; -	bool connector_hdmi; +	bool restart_stdi_once;  	bool hdmi_port_a;  	/* i2c clients */ @@ -491,6 +495,11 @@ static inline int hdmi_write(struct v4l2_subdev *sd, u8 reg, u8 val)  	return adv_smbus_write_byte_data(state->i2c_hdmi, reg, val);  } +static inline int hdmi_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +{ +	return hdmi_write(sd, reg, (hdmi_read(sd, reg) & mask) | val); +} +  static inline int cp_read(struct v4l2_subdev *sd, u8 reg)  {  	struct adv7842_state *state = to_state(sd); @@ -532,11 +541,19 @@ static void main_reset(struct v4l2_subdev *sd)  	adv_smbus_write_byte_no_check(client, 0xff, 0x80); -	mdelay(2); +	mdelay(5);  }  /* ----------------------------------------------------------------------- */ +static inline bool is_analog_input(struct v4l2_subdev *sd) +{ +	struct adv7842_state *state = to_state(sd); + +	return ((state->mode == ADV7842_MODE_RGB) || +		(state->mode == ADV7842_MODE_COMP)); +} +  static inline bool is_digital_input(struct v4l2_subdev *sd)  {  	struct adv7842_state *state = to_state(sd); @@ -546,30 +563,24 @@ static inline bool is_digital_input(struct v4l2_subdev *sd)  static const struct v4l2_dv_timings_cap adv7842_timings_cap_analog = {  	.type = V4L2_DV_BT_656_1120, -	.bt = { -		.max_width = 1920, -		.max_height = 1200, -		.min_pixelclock = 25000000, -		.max_pixelclock = 170000000, -		.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, 1920, 0, 1200, 25000000, 170000000, +		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 const struct v4l2_dv_timings_cap adv7842_timings_cap_digital = {  	.type = V4L2_DV_BT_656_1120, -	.bt = { -		.max_width = 1920, -		.max_height = 1200, -		.min_pixelclock = 25000000, -		.max_pixelclock = 225000000, -		.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, 1920, 0, 1200, 25000000, 225000000, +		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 const struct v4l2_dv_timings_cap * @@ -593,10 +604,10 @@ static void adv7842_delayed_work_enable_hotplug(struct work_struct *work)  	v4l2_dbg(2, debug, sd, "%s: enable hotplug on ports: 0x%x\n",  			__func__, present); -	if (present & 0x1) -		mask |= 0x20; /* port A */ -	if (present & 0x2) -		mask |= 0x10; /* port B */ +	if (present & (0x04 << ADV7842_EDID_PORT_A)) +		mask |= 0x20; +	if (present & (0x04 << ADV7842_EDID_PORT_B)) +		mask |= 0x10;  	io_write_and_or(sd, 0x20, 0xcf, mask);  } @@ -685,14 +696,12 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port)  	struct i2c_client *client = v4l2_get_subdevdata(sd);  	struct adv7842_state *state = to_state(sd);  	const u8 *val = state->hdmi_edid.edid; -	u8 cur_mask = rep_read(sd, 0x77) & 0x0c; -	u8 mask = port == 0 ? 0x4 : 0x8;  	int spa_loc = edid_spa_location(val);  	int err = 0;  	int i; -	v4l2_dbg(2, debug, sd, "%s: write EDID on port %d (spa at 0x%x)\n", -			__func__, port, spa_loc); +	v4l2_dbg(2, debug, sd, "%s: write EDID on port %c (spa at 0x%x)\n", +			__func__, (port == ADV7842_EDID_PORT_A) ? 'A' : 'B', spa_loc);  	/* HPA disable on port A and B */  	io_write_and_or(sd, 0x20, 0xcf, 0x00); @@ -700,6 +709,9 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port)  	/* Disable I2C access to internal EDID ram from HDMI DDC ports */  	rep_write_and_or(sd, 0x77, 0xf3, 0x00); +	if (!state->hdmi_edid.present) +		return 0; +  	/* edid segment pointer '0' for HDMI ports */  	rep_write_and_or(sd, 0x77, 0xef, 0x00); @@ -709,44 +721,32 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port)  	if (err)  		return err; -	if (spa_loc > 0) { -		if (port == 0) { -			/* port A SPA */ -			rep_write(sd, 0x72, val[spa_loc]); -			rep_write(sd, 0x73, val[spa_loc + 1]); -		} else { -			/* port B SPA */ -			rep_write(sd, 0x74, val[spa_loc]); -			rep_write(sd, 0x75, val[spa_loc + 1]); -		} -		rep_write(sd, 0x76, spa_loc); +	if (spa_loc < 0) +		spa_loc = 0xc0; /* Default value [REF_02, p. 199] */ + +	if (port == ADV7842_EDID_PORT_A) { +		rep_write(sd, 0x72, val[spa_loc]); +		rep_write(sd, 0x73, val[spa_loc + 1]);  	} else { -		/* default register values for SPA */ -		if (port == 0) { -			/* port A SPA */ -			rep_write(sd, 0x72, 0); -			rep_write(sd, 0x73, 0); -		} else { -			/* port B SPA */ -			rep_write(sd, 0x74, 0); -			rep_write(sd, 0x75, 0); -		} -		rep_write(sd, 0x76, 0xc0); +		rep_write(sd, 0x74, val[spa_loc]); +		rep_write(sd, 0x75, val[spa_loc + 1]);  	} -	rep_write_and_or(sd, 0x77, 0xbf, 0x00); +	rep_write(sd, 0x76, spa_loc & 0xff); +	rep_write_and_or(sd, 0x77, 0xbf, (spa_loc >> 2) & 0x40);  	/* Calculates the checksums and enables I2C access to internal  	 * EDID ram from HDMI DDC ports  	 */ -	rep_write_and_or(sd, 0x77, 0xf3, mask | cur_mask); +	rep_write_and_or(sd, 0x77, 0xf3, state->hdmi_edid.present);  	for (i = 0; i < 1000; i++) { -		if (rep_read(sd, 0x7d) & mask) +		if (rep_read(sd, 0x7d) & state->hdmi_edid.present)  			break;  		mdelay(1);  	}  	if (i == 1000) { -		v4l_err(client, "error enabling edid on port %d\n", port); +		v4l_err(client, "error enabling edid on port %c\n", +				(port == ADV7842_EDID_PORT_A) ? 'A' : 'B');  		return -EIO;  	} @@ -933,7 +933,7 @@ static int configure_predefined_video_timings(struct v4l2_subdev *sd,  	cp_write(sd, 0x27, 0x00);  	cp_write(sd, 0x28, 0x00);  	cp_write(sd, 0x29, 0x00); -	cp_write(sd, 0x8f, 0x00); +	cp_write(sd, 0x8f, 0x40);  	cp_write(sd, 0x90, 0x00);  	cp_write(sd, 0xa5, 0x00);  	cp_write(sd, 0xa6, 0x00); @@ -1019,7 +1019,7 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd,  		break;  	case ADV7842_MODE_HDMI:  		/* set default prim_mode/vid_std for HDMI -		   accoring to [REF_03, c. 4.2] */ +		   according to [REF_03, c. 4.2] */  		io_write(sd, 0x00, 0x02); /* video std */  		io_write(sd, 0x01, 0x06); /* prim mode */  		break; @@ -1035,38 +1035,145 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd,  	cp_write(sd, 0xac, (height & 0x0f) << 4);  } +static void adv7842_set_offset(struct v4l2_subdev *sd, bool auto_offset, u16 offset_a, u16 offset_b, u16 offset_c) +{ +	struct adv7842_state *state = to_state(sd); +	u8 offset_buf[4]; + +	if (auto_offset) { +		offset_a = 0x3ff; +		offset_b = 0x3ff; +		offset_c = 0x3ff; +	} + +	v4l2_dbg(2, debug, sd, "%s: %s offset: a = 0x%x, b = 0x%x, c = 0x%x\n", +		 __func__, auto_offset ? "Auto" : "Manual", +		 offset_a, offset_b, offset_c); + +	offset_buf[0]= (cp_read(sd, 0x77) & 0xc0) | ((offset_a & 0x3f0) >> 4); +	offset_buf[1] = ((offset_a & 0x00f) << 4) | ((offset_b & 0x3c0) >> 6); +	offset_buf[2] = ((offset_b & 0x03f) << 2) | ((offset_c & 0x300) >> 8); +	offset_buf[3] = offset_c & 0x0ff; + +	/* Registers must be written in this order with no i2c access in between */ +	if (adv_smbus_write_i2c_block_data(state->i2c_cp, 0x77, 4, offset_buf)) +		v4l2_err(sd, "%s: i2c error writing to CP reg 0x77, 0x78, 0x79, 0x7a\n", __func__); +} + +static void adv7842_set_gain(struct v4l2_subdev *sd, bool auto_gain, u16 gain_a, u16 gain_b, u16 gain_c) +{ +	struct adv7842_state *state = to_state(sd); +	u8 gain_buf[4]; +	u8 gain_man = 1; +	u8 agc_mode_man = 1; + +	if (auto_gain) { +		gain_man = 0; +		agc_mode_man = 0; +		gain_a = 0x100; +		gain_b = 0x100; +		gain_c = 0x100; +	} + +	v4l2_dbg(2, debug, sd, "%s: %s gain: a = 0x%x, b = 0x%x, c = 0x%x\n", +		 __func__, auto_gain ? "Auto" : "Manual", +		 gain_a, gain_b, gain_c); + +	gain_buf[0] = ((gain_man << 7) | (agc_mode_man << 6) | ((gain_a & 0x3f0) >> 4)); +	gain_buf[1] = (((gain_a & 0x00f) << 4) | ((gain_b & 0x3c0) >> 6)); +	gain_buf[2] = (((gain_b & 0x03f) << 2) | ((gain_c & 0x300) >> 8)); +	gain_buf[3] = ((gain_c & 0x0ff)); + +	/* Registers must be written in this order with no i2c access in between */ +	if (adv_smbus_write_i2c_block_data(state->i2c_cp, 0x73, 4, gain_buf)) +		v4l2_err(sd, "%s: i2c error writing to CP reg 0x73, 0x74, 0x75, 0x76\n", __func__); +} +  static void set_rgb_quantization_range(struct v4l2_subdev *sd)  {  	struct adv7842_state *state = to_state(sd); +	bool rgb_output = io_read(sd, 0x02) & 0x02; +	bool hdmi_signal = hdmi_read(sd, 0x05) & 0x80; + +	v4l2_dbg(2, debug, sd, "%s: RGB quantization range: %d, RGB out: %d, HDMI: %d\n", +			__func__, state->rgb_quantization_range, +			rgb_output, hdmi_signal); + +	adv7842_set_gain(sd, true, 0x0, 0x0, 0x0); +	adv7842_set_offset(sd, true, 0x0, 0x0, 0x0);  	switch (state->rgb_quantization_range) {  	case V4L2_DV_RGB_RANGE_AUTO: -		/* automatic */ -		if (is_digital_input(sd) && !(hdmi_read(sd, 0x05) & 0x80)) { -			/* receiving DVI-D signal */ +		if (state->mode == ADV7842_MODE_RGB) { +			/* Receiving analog RGB signal +			 * Set RGB full range (0-255) */ +			io_write_and_or(sd, 0x02, 0x0f, 0x10); +			break; +		} + +		if (state->mode == ADV7842_MODE_COMP) { +			/* Receiving analog YPbPr signal +			 * Set automode */ +			io_write_and_or(sd, 0x02, 0x0f, 0xf0); +			break; +		} + +		if (hdmi_signal) { +			/* Receiving HDMI signal +			 * Set automode */ +			io_write_and_or(sd, 0x02, 0x0f, 0xf0); +			break; +		} -			/* ADV7842 selects RGB limited range regardless of -			   input format (CE/IT) in automatic mode */ -			if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) { -				/* RGB limited range (16-235) */ -				io_write_and_or(sd, 0x02, 0x0f, 0x00); +		/* Receiving DVI-D signal +		 * ADV7842 selects RGB limited range regardless of +		 * input format (CE/IT) in automatic mode */ +		if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) { +			/* RGB limited range (16-235) */ +			io_write_and_or(sd, 0x02, 0x0f, 0x00); +		} else { +			/* RGB full range (0-255) */ +			io_write_and_or(sd, 0x02, 0x0f, 0x10); +			if (is_digital_input(sd) && rgb_output) { +				adv7842_set_offset(sd, false, 0x40, 0x40, 0x40);  			} else { -				/* RGB full range (0-255) */ -				io_write_and_or(sd, 0x02, 0x0f, 0x10); +				adv7842_set_gain(sd, false, 0xe0, 0xe0, 0xe0); +				adv7842_set_offset(sd, false, 0x70, 0x70, 0x70);  			} -		} else { -			/* receiving HDMI or analog signal, set automode */ -			io_write_and_or(sd, 0x02, 0x0f, 0xf0);  		}  		break;  	case V4L2_DV_RGB_RANGE_LIMITED: +		if (state->mode == ADV7842_MODE_COMP) { +			/* YCrCb limited range (16-235) */ +			io_write_and_or(sd, 0x02, 0x0f, 0x20); +			break; +		} +  		/* RGB limited range (16-235) */  		io_write_and_or(sd, 0x02, 0x0f, 0x00); +  		break;  	case V4L2_DV_RGB_RANGE_FULL: +		if (state->mode == ADV7842_MODE_COMP) { +			/* YCrCb full range (0-255) */ +			io_write_and_or(sd, 0x02, 0x0f, 0x60); +			break; +		} +  		/* RGB full range (0-255) */  		io_write_and_or(sd, 0x02, 0x0f, 0x10); + +		if (is_analog_input(sd) || hdmi_signal) +			break; + +		/* Adjust gain/offset for DVI-D signals only */ +		if (rgb_output) { +			adv7842_set_offset(sd, false, 0x40, 0x40, 0x40); +		} else { +			adv7842_set_gain(sd, false, 0xe0, 0xe0, 0xe0); +			adv7842_set_offset(sd, false, 0x70, 0x70, 0x70); +		}  		break;  	}  } @@ -1292,6 +1399,9 @@ static int read_stdi(struct v4l2_subdev *sd, struct stdi_readback *stdi)  static int adv7842_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,  		adv7842_get_dv_timings_cap(sd), adv7842_check_dv_timings, NULL);  } @@ -1299,12 +1409,15 @@ static int adv7842_enum_dv_timings(struct v4l2_subdev *sd,  static int adv7842_dv_timings_cap(struct v4l2_subdev *sd,  				  struct v4l2_dv_timings_cap *cap)  { +	if (cap->pad != 0) +		return -EINVAL; +  	*cap = *adv7842_get_dv_timings_cap(sd);  	return 0;  }  /* Fill the optional fields .standards and .flags in struct v4l2_dv_timings -   if the format is listed in adv7604_timings[] */ +   if the format is listed in adv7842_timings[] */  static void adv7842_fill_optional_dv_timings_fields(struct v4l2_subdev *sd,  		struct v4l2_dv_timings *timings)  { @@ -1320,119 +1433,105 @@ static int adv7842_query_dv_timings(struct v4l2_subdev *sd,  	struct v4l2_bt_timings *bt = &timings->bt;  	struct stdi_readback stdi = { 0 }; +	v4l2_dbg(1, debug, sd, "%s:\n", __func__); +  	/* SDP block */  	if (state->mode == ADV7842_MODE_SDP)  		return -ENODATA;  	/* read STDI */  	if (read_stdi(sd, &stdi)) { +		state->restart_stdi_once = true;  		v4l2_dbg(1, debug, sd, "%s: no valid signal\n", __func__);  		return -ENOLINK;  	}  	bt->interlaced = stdi.interlaced ?  		V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; -	bt->polarities = ((hdmi_read(sd, 0x05) & 0x10) ? V4L2_DV_VSYNC_POS_POL : 0) | -		((hdmi_read(sd, 0x05) & 0x20) ? V4L2_DV_HSYNC_POS_POL : 0); -	bt->vsync = stdi.lcvs;  	if (is_digital_input(sd)) { -		bool lock = hdmi_read(sd, 0x04) & 0x02; -		bool interlaced = hdmi_read(sd, 0x0b) & 0x20; -		unsigned w = (hdmi_read(sd, 0x07) & 0x1f) * 256 + hdmi_read(sd, 0x08); -		unsigned h = (hdmi_read(sd, 0x09) & 0x1f) * 256 + hdmi_read(sd, 0x0a); -		unsigned w_total = (hdmi_read(sd, 0x1e) & 0x3f) * 256 + -			hdmi_read(sd, 0x1f); -		unsigned h_total = ((hdmi_read(sd, 0x26) & 0x3f) * 256 + -				    hdmi_read(sd, 0x27)) / 2; -		unsigned freq = (((hdmi_read(sd, 0x51) << 1) + -					(hdmi_read(sd, 0x52) >> 7)) * 1000000) + -			((hdmi_read(sd, 0x52) & 0x7f) * 1000000) / 128; -		int i; +		uint32_t freq; + +		timings->type = V4L2_DV_BT_656_1120; +		bt->width = (hdmi_read(sd, 0x07) & 0x0f) * 256 + hdmi_read(sd, 0x08); +		bt->height = (hdmi_read(sd, 0x09) & 0x0f) * 256 + hdmi_read(sd, 0x0a); +		freq = ((hdmi_read(sd, 0x51) << 1) + (hdmi_read(sd, 0x52) >> 7)) * 1000000; +		freq += ((hdmi_read(sd, 0x52) & 0x7f) * 7813);  		if (is_hdmi(sd)) {  			/* adjust for deep color mode */ -			freq = freq * 8 / (((hdmi_read(sd, 0x0b) & 0xc0)>>6) * 2 + 8); -		} - -		/* No lock? */ -		if (!lock) { -			v4l2_dbg(1, debug, sd, "%s: no lock on TMDS signal\n", __func__); -			return -ENOLCK; +			freq = freq * 8 / (((hdmi_read(sd, 0x0b) & 0xc0) >> 6) * 2 + 8);  		} -		/* Interlaced? */ -		if (interlaced) { -			v4l2_dbg(1, debug, sd, "%s: interlaced video not supported\n", __func__); -			return -ERANGE; -		} - -		for (i = 0; v4l2_dv_timings_presets[i].bt.width; i++) { -			const struct v4l2_bt_timings *bt = &v4l2_dv_timings_presets[i].bt; - -			if (!v4l2_valid_dv_timings(&v4l2_dv_timings_presets[i], -						   adv7842_get_dv_timings_cap(sd), -						   adv7842_check_dv_timings, NULL)) -				continue; -			if (w_total != htotal(bt) || h_total != vtotal(bt)) -				continue; - -			if (w != bt->width || h != bt->height) -				continue; - -			if (abs(freq - bt->pixelclock) > 1000000) -				continue; -			*timings = v4l2_dv_timings_presets[i]; -			return 0; -		} - -		timings->type = V4L2_DV_BT_656_1120; - -		bt->width = w; -		bt->height = h; -		bt->interlaced = (hdmi_read(sd, 0x0b) & 0x20) ? -			V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; -		bt->polarities = ((hdmi_read(sd, 0x05) & 0x10) ? -			V4L2_DV_VSYNC_POS_POL : 0) | ((hdmi_read(sd, 0x05) & 0x20) ? -			V4L2_DV_HSYNC_POS_POL : 0); -		bt->pixelclock = (((hdmi_read(sd, 0x51) << 1) + -				   (hdmi_read(sd, 0x52) >> 7)) * 1000000) + -				 ((hdmi_read(sd, 0x52) & 0x7f) * 1000000) / 128; -		bt->hfrontporch = (hdmi_read(sd, 0x20) & 0x1f) * 256 + +		bt->pixelclock = freq; +		bt->hfrontporch = (hdmi_read(sd, 0x20) & 0x03) * 256 +  			hdmi_read(sd, 0x21); -		bt->hsync = (hdmi_read(sd, 0x22) & 0x1f) * 256 + +		bt->hsync = (hdmi_read(sd, 0x22) & 0x03) * 256 +  			hdmi_read(sd, 0x23); -		bt->hbackporch = (hdmi_read(sd, 0x24) & 0x1f) * 256 + +		bt->hbackporch = (hdmi_read(sd, 0x24) & 0x03) * 256 +  			hdmi_read(sd, 0x25); -		bt->vfrontporch = ((hdmi_read(sd, 0x2a) & 0x3f) * 256 + -				   hdmi_read(sd, 0x2b)) / 2; -		bt->il_vfrontporch = ((hdmi_read(sd, 0x2c) & 0x3f) * 256 + -				      hdmi_read(sd, 0x2d)) / 2; -		bt->vsync = ((hdmi_read(sd, 0x2e) & 0x3f) * 256 + -			     hdmi_read(sd, 0x2f)) / 2; -		bt->il_vsync = ((hdmi_read(sd, 0x30) & 0x3f) * 256 + -				hdmi_read(sd, 0x31)) / 2; -		bt->vbackporch = ((hdmi_read(sd, 0x32) & 0x3f) * 256 + -				  hdmi_read(sd, 0x33)) / 2; -		bt->il_vbackporch = ((hdmi_read(sd, 0x34) & 0x3f) * 256 + -				     hdmi_read(sd, 0x35)) / 2; - -		bt->standards = 0; -		bt->flags = 0; -	} else { -		/* Interlaced? */ -		if (stdi.interlaced) { -			v4l2_dbg(1, debug, sd, "%s: interlaced video not supported\n", __func__); -			return -ERANGE; +		bt->vfrontporch = ((hdmi_read(sd, 0x2a) & 0x1f) * 256 + +			hdmi_read(sd, 0x2b)) / 2; +		bt->vsync = ((hdmi_read(sd, 0x2e) & 0x1f) * 256 + +			hdmi_read(sd, 0x2f)) / 2; +		bt->vbackporch = ((hdmi_read(sd, 0x32) & 0x1f) * 256 + +			hdmi_read(sd, 0x33)) / 2; +		bt->polarities = ((hdmi_read(sd, 0x05) & 0x10) ? V4L2_DV_VSYNC_POS_POL : 0) | +			((hdmi_read(sd, 0x05) & 0x20) ? V4L2_DV_HSYNC_POS_POL : 0); +		if (bt->interlaced == V4L2_DV_INTERLACED) { +			bt->height += (hdmi_read(sd, 0x0b) & 0x0f) * 256 + +					hdmi_read(sd, 0x0c); +			bt->il_vfrontporch = ((hdmi_read(sd, 0x2c) & 0x1f) * 256 + +					hdmi_read(sd, 0x2d)) / 2; +			bt->il_vsync = ((hdmi_read(sd, 0x30) & 0x1f) * 256 + +					hdmi_read(sd, 0x31)) / 2; +			bt->vbackporch = ((hdmi_read(sd, 0x34) & 0x1f) * 256 + +					hdmi_read(sd, 0x35)) / 2;  		} - +		adv7842_fill_optional_dv_timings_fields(sd, timings); +	} else { +		/* find format +		 * Since LCVS values are inaccurate [REF_03, p. 339-340], +		 * stdi2dv_timings() is called with lcvs +-1 if the first attempt fails. +		 */ +		if (!stdi2dv_timings(sd, &stdi, timings)) +			goto found; +		stdi.lcvs += 1; +		v4l2_dbg(1, debug, sd, "%s: lcvs + 1 = %d\n", __func__, stdi.lcvs); +		if (!stdi2dv_timings(sd, &stdi, timings)) +			goto found; +		stdi.lcvs -= 2; +		v4l2_dbg(1, debug, sd, "%s: lcvs - 1 = %d\n", __func__, stdi.lcvs);  		if (stdi2dv_timings(sd, &stdi, timings)) { +			/* +			 * The STDI block may measure wrong values, especially +			 * for lcvs and lcf. If the driver can not find any +			 * valid timing, the STDI block is restarted to measure +			 * the video timings again. The function will return an +			 * error, but the restart of STDI will generate a new +			 * STDI interrupt and the format detection process will +			 * restart. +			 */ +			if (state->restart_stdi_once) { +				v4l2_dbg(1, debug, sd, "%s: restart STDI\n", __func__); +				/* TODO restart STDI for Sync Channel 2 */ +				/* enter one-shot mode */ +				cp_write_and_or(sd, 0x86, 0xf9, 0x00); +				/* trigger STDI restart */ +				cp_write_and_or(sd, 0x86, 0xf9, 0x04); +				/* reset to continuous mode */ +				cp_write_and_or(sd, 0x86, 0xf9, 0x02); +				state->restart_stdi_once = false; +				return -ENOLINK; +			}  			v4l2_dbg(1, debug, sd, "%s: format not supported\n", __func__);  			return -ERANGE;  		} +		state->restart_stdi_once = true;  	} +found:  	if (debug > 1) -		v4l2_print_dv_timings(sd->name, "adv7842_query_dv_timings: ", -				      timings, true); +		v4l2_print_dv_timings(sd->name, "adv7842_query_dv_timings:", +				timings, true);  	return 0;  } @@ -1443,9 +1542,16 @@ static int adv7842_s_dv_timings(struct v4l2_subdev *sd,  	struct v4l2_bt_timings *bt;  	int err; +	v4l2_dbg(1, debug, sd, "%s:\n", __func__); +  	if (state->mode == ADV7842_MODE_SDP)  		return -ENODATA; +	if (v4l2_match_dv_timings(&state->timings, timings, 0)) { +		v4l2_dbg(1, debug, sd, "%s: no change\n", __func__); +		return 0; +	} +  	bt = &timings->bt;  	if (!v4l2_valid_dv_timings(timings, adv7842_get_dv_timings_cap(sd), @@ -1456,7 +1562,7 @@ static int adv7842_s_dv_timings(struct v4l2_subdev *sd,  	state->timings = *timings; -	cp_write(sd, 0x91, bt->interlaced ? 0x50 : 0x10); +	cp_write(sd, 0x91, bt->interlaced ? 0x40 : 0x00);  	/* Use prim_mode and vid_std when available */  	err = configure_predefined_video_timings(sd, timings); @@ -1489,18 +1595,18 @@ static int adv7842_g_dv_timings(struct v4l2_subdev *sd,  static void enable_input(struct v4l2_subdev *sd)  {  	struct adv7842_state *state = to_state(sd); + +	set_rgb_quantization_range(sd);  	switch (state->mode) {  	case ADV7842_MODE_SDP:  	case ADV7842_MODE_COMP:  	case ADV7842_MODE_RGB: -		/* enable */  		io_write(sd, 0x15, 0xb0);   /* Disable Tristate of Pins (no audio) */  		break;  	case ADV7842_MODE_HDMI: -		/* enable */ -		hdmi_write(sd, 0x1a, 0x0a); /* Unmute audio */  		hdmi_write(sd, 0x01, 0x00); /* Enable HDMI clock terminators */  		io_write(sd, 0x15, 0xa0);   /* Disable Tristate of Pins */ +		hdmi_write_and_or(sd, 0x1a, 0xef, 0x00); /* Unmute audio */  		break;  	default:  		v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", @@ -1511,9 +1617,9 @@ static void enable_input(struct v4l2_subdev *sd)  static void disable_input(struct v4l2_subdev *sd)  { -	/* disable */ +	hdmi_write_and_or(sd, 0x1a, 0xef, 0x10); /* Mute audio [REF_01, c. 2.2.2] */ +	msleep(16); /* 512 samples with >= 32 kHz sample rate [REF_03, c. 8.29] */  	io_write(sd, 0x15, 0xbe);   /* Tristate all outputs from video core */ -	hdmi_write(sd, 0x1a, 0x1a); /* Mute audio */  	hdmi_write(sd, 0x01, 0x78); /* Disable HDMI clock terminators */  } @@ -1581,9 +1687,6 @@ static void select_input(struct v4l2_subdev *sd,  		afe_write(sd, 0x00, 0x00); /* power up ADC */  		afe_write(sd, 0xc8, 0x00); /* phase control */ -		io_write(sd, 0x19, 0x83); /* LLC DLL phase */ -		io_write(sd, 0x33, 0x40); /* LLC DLL enable */ -  		io_write(sd, 0xdd, 0x90); /* Manual 2x output clock */  		/* script says register 0xde, which don't exist in manual */ @@ -1617,8 +1720,6 @@ static void select_input(struct v4l2_subdev *sd,  		/* deinterlacer enabled and 3D comb */  		sdp_write_and_or(sd, 0x12, 0xf6, 0x09); -		sdp_write(sd, 0xdd, 0x08); /* free run auto */ -  		break;  	case ADV7842_MODE_COMP: @@ -1633,6 +1734,13 @@ static void select_input(struct v4l2_subdev *sd,  		afe_write(sd, 0x00, 0x00); /* power up ADC */  		afe_write(sd, 0xc8, 0x00); /* phase control */ +		if (state->mode == ADV7842_MODE_COMP) { +			/* force to YCrCb */ +			io_write_and_or(sd, 0x02, 0x0f, 0x60); +		} else { +			/* force to RGB */ +			io_write_and_or(sd, 0x02, 0x0f, 0x10); +		}  		/* set ADI recommended settings for digitizer */  		/* "ADV7842 Register Settings Recommendations @@ -1703,8 +1811,8 @@ static void select_input(struct v4l2_subdev *sd,  		 * (rev. 2.5, June 2010)" p. 17. */  		afe_write(sd, 0x12, 0xfb); /* ADC noise shaping filter controls */  		afe_write(sd, 0x0c, 0x0d); /* CP core gain controls */ -		cp_write(sd, 0x3e, 0x80); /* CP core pre-gain control, -					     enable color control */ +		cp_write(sd, 0x3e, 0x00); /* CP core pre-gain control */ +  		/* CP coast control */  		cp_write(sd, 0xc3, 0x33); /* Component mode */ @@ -1728,19 +1836,19 @@ static int adv7842_s_routing(struct v4l2_subdev *sd,  	switch (input) {  	case ADV7842_SELECT_HDMI_PORT_A: -		/* TODO select HDMI_COMP or HDMI_GR */  		state->mode = ADV7842_MODE_HDMI;  		state->vid_std_select = ADV7842_HDMI_COMP_VID_STD_HD_1250P;  		state->hdmi_port_a = true;  		break;  	case ADV7842_SELECT_HDMI_PORT_B: -		/* TODO select HDMI_COMP or HDMI_GR */  		state->mode = ADV7842_MODE_HDMI;  		state->vid_std_select = ADV7842_HDMI_COMP_VID_STD_HD_1250P;  		state->hdmi_port_a = false;  		break;  	case ADV7842_SELECT_VGA_COMP: -		v4l2_info(sd, "%s: VGA component: todo\n", __func__); +		state->mode = ADV7842_MODE_COMP; +		state->vid_std_select = ADV7842_RGB_VID_STD_AUTO_GRAPH_MODE; +		break;  	case ADV7842_SELECT_VGA_RGB:  		state->mode = ADV7842_MODE_RGB;  		state->vid_std_select = ADV7842_RGB_VID_STD_AUTO_GRAPH_MODE; @@ -1820,12 +1928,15 @@ static void adv7842_irq_enable(struct v4l2_subdev *sd, bool enable)  		io_write(sd, 0x78, 0x03);  		/* Enable SDP Standard Detection Change and SDP Video Detected */  		io_write(sd, 0xa0, 0x09); +		/* Enable HDMI_MODE interrupt */ +		io_write(sd, 0x69, 0x08);  	} else {  		io_write(sd, 0x46, 0x0);  		io_write(sd, 0x5a, 0x0);  		io_write(sd, 0x73, 0x0);  		io_write(sd, 0x78, 0x0);  		io_write(sd, 0xa0, 0x0); +		io_write(sd, 0x69, 0x0);  	}  } @@ -1833,11 +1944,9 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled)  {  	struct adv7842_state *state = to_state(sd);  	u8 fmt_change_cp, fmt_change_digital, fmt_change_sdp; -	u8 irq_status[5]; -	u8 irq_cfg = io_read(sd, 0x40); +	u8 irq_status[6]; -	/* disable irq-pin output */ -	io_write(sd, 0x40, irq_cfg | 0x3); +	adv7842_irq_enable(sd, false);  	/* read status */  	irq_status[0] = io_read(sd, 0x43); @@ -1845,6 +1954,7 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled)  	irq_status[2] = io_read(sd, 0x70);  	irq_status[3] = io_read(sd, 0x75);  	irq_status[4] = io_read(sd, 0x9d); +	irq_status[5] = io_read(sd, 0x66);  	/* and clear */  	if (irq_status[0]) @@ -1857,10 +1967,14 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled)  		io_write(sd, 0x76, irq_status[3]);  	if (irq_status[4])  		io_write(sd, 0x9e, irq_status[4]); +	if (irq_status[5]) +		io_write(sd, 0x67, irq_status[5]); + +	adv7842_irq_enable(sd, true); -	v4l2_dbg(1, debug, sd, "%s: irq %x, %x, %x, %x, %x\n", __func__, +	v4l2_dbg(1, debug, sd, "%s: irq %x, %x, %x, %x, %x, %x\n", __func__,  		 irq_status[0], irq_status[1], irq_status[2], -		 irq_status[3], irq_status[4]); +		 irq_status[3], irq_status[4], irq_status[5]);  	/* format change CP */  	fmt_change_cp = irq_status[0] & 0x9c; @@ -1877,60 +1991,109 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled)  	else  		fmt_change_digital = 0; -	/* notify */ +	/* format change */  	if (fmt_change_cp || fmt_change_digital || fmt_change_sdp) {  		v4l2_dbg(1, debug, sd,  			 "%s: fmt_change_cp = 0x%x, fmt_change_digital = 0x%x, fmt_change_sdp = 0x%x\n",  			 __func__, fmt_change_cp, fmt_change_digital,  			 fmt_change_sdp);  		v4l2_subdev_notify(sd, ADV7842_FMT_CHANGE, NULL); +		if (handled) +			*handled = true;  	} -	/* 5v cable detect */ -	if (irq_status[2]) +	/* HDMI/DVI mode */ +	if (irq_status[5] & 0x08) { +		v4l2_dbg(1, debug, sd, "%s: irq %s mode\n", __func__, +			 (io_read(sd, 0x65) & 0x08) ? "HDMI" : "DVI"); +		set_rgb_quantization_range(sd); +		if (handled) +			*handled = true; +	} + +	/* tx 5v detect */ +	if (irq_status[2] & 0x3) { +		v4l2_dbg(1, debug, sd, "%s: irq tx_5v\n", __func__);  		adv7842_s_detect_tx_5v_ctrl(sd); +		if (handled) +			*handled = true; +	} +	return 0; +} -	if (handled) -		*handled = true; +static int adv7842_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) +{ +	struct adv7842_state *state = to_state(sd); +	u8 *data = NULL; -	/* re-enable irq-pin output */ -	io_write(sd, 0x40, irq_cfg); +	if (edid->pad > ADV7842_EDID_PORT_VGA) +		return -EINVAL; +	if (edid->blocks == 0) +		return -EINVAL; +	if (edid->blocks > 2) +		return -EINVAL; +	if (edid->start_block > 1) +		return -EINVAL; +	if (edid->start_block == 1) +		edid->blocks = 1; + +	switch (edid->pad) { +	case ADV7842_EDID_PORT_A: +	case ADV7842_EDID_PORT_B: +		if (state->hdmi_edid.present & (0x04 << edid->pad)) +			data = state->hdmi_edid.edid; +		break; +	case ADV7842_EDID_PORT_VGA: +		if (state->vga_edid.present) +			data = state->vga_edid.edid; +		break; +	default: +		return -EINVAL; +	} +	if (!data) +		return -ENODATA; +	memcpy(edid->edid, +	       data + edid->start_block * 128, +	       edid->blocks * 128);  	return 0;  } -static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *e) +static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *e)  {  	struct adv7842_state *state = to_state(sd);  	int err = 0; -	if (e->pad > 2) +	if (e->pad > ADV7842_EDID_PORT_VGA)  		return -EINVAL;  	if (e->start_block != 0)  		return -EINVAL;  	if (e->blocks > 2)  		return -E2BIG; -	if (!e->edid) -		return -EINVAL;  	/* todo, per edid */  	state->aspect_ratio = v4l2_calc_aspect_ratio(e->edid[0x15],  			e->edid[0x16]); -	if (e->pad == 2) { +	switch (e->pad) { +	case ADV7842_EDID_PORT_VGA:  		memset(&state->vga_edid.edid, 0, 256);  		state->vga_edid.present = e->blocks ? 0x1 : 0x0;  		memcpy(&state->vga_edid.edid, e->edid, 128 * e->blocks);  		err = edid_write_vga_segment(sd); -	} else { -		u32 mask = 0x1<<e->pad; +		break; +	case ADV7842_EDID_PORT_A: +	case ADV7842_EDID_PORT_B:  		memset(&state->hdmi_edid.edid, 0, 256);  		if (e->blocks) -			state->hdmi_edid.present |= mask; +			state->hdmi_edid.present |= 0x04 << e->pad;  		else -			state->hdmi_edid.present &= ~mask; -		memcpy(&state->hdmi_edid.edid, e->edid, 128*e->blocks); +			state->hdmi_edid.present &= ~(0x04 << e->pad); +		memcpy(&state->hdmi_edid.edid, e->edid, 128 * e->blocks);  		err = edid_write_hdmi_segment(sd, e->pad); +		break; +	default: +		return -EINVAL;  	}  	if (err < 0)  		v4l2_err(sd, "error %d writing edid on port %d\n", err, e->pad); @@ -2031,7 +2194,8 @@ static void print_avi_infoframe(struct v4l2_subdev *sd)  {  	int i;  	uint8_t buf[14]; -	uint8_t avi_inf_len; +	u8 avi_len; +	u8 avi_ver;  	struct avi_info_frame avi;  	if (!(hdmi_read(sd, 0x05) & 0x80)) { @@ -2044,18 +2208,20 @@ static void print_avi_infoframe(struct v4l2_subdev *sd)  	}  	if (io_read(sd, 0x88) & 0x10) { -		/* Note: the ADV7842 calculated incorrect checksums for InfoFrames -		   with a length of 14 or 15. See the ADV7842 Register Settings -		   Recommendations document for more details. */ -		v4l2_info(sd, "AVI infoframe checksum error\n"); -		return; +		v4l2_info(sd, "AVI infoframe checksum error has occurred earlier\n"); +		io_write(sd, 0x8a, 0x10); /* clear AVI_INF_CKS_ERR_RAW */ +		if (io_read(sd, 0x88) & 0x10) { +			v4l2_info(sd, "AVI infoframe checksum error still present\n"); +			io_write(sd, 0x8a, 0x10); /* clear AVI_INF_CKS_ERR_RAW */ +		}  	} -	avi_inf_len = infoframe_read(sd, 0xe2); +	avi_len = infoframe_read(sd, 0xe2); +	avi_ver = infoframe_read(sd, 0xe1);  	v4l2_info(sd, "AVI infoframe version %d (%d byte)\n", -		  infoframe_read(sd, 0xe1), avi_inf_len); +		  avi_ver, avi_len); -	if (infoframe_read(sd, 0xe1) != 0x02) +	if (avi_ver != 0x02)  		return;  	for (i = 0; i < 14; i++) @@ -2162,7 +2328,7 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd)  	static const char * const input_color_space_txt[16] = {  		"RGB limited range (16-235)", "RGB full range (0-255)",  		"YCbCr Bt.601 (16-235)", "YCbCr Bt.709 (16-235)", -		"XvYCC Bt.601", "XvYCC Bt.709", +		"xvYCC Bt.601", "xvYCC Bt.709",  		"YCbCr Bt.601 (0-255)", "YCbCr Bt.709 (0-255)",  		"invalid", "invalid", "invalid", "invalid", "invalid",  		"invalid", "invalid", "automatic" @@ -2181,8 +2347,6 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd)  	v4l2_info(sd, "-----Chip status-----\n");  	v4l2_info(sd, "Chip power: %s\n", no_power(sd) ? "off" : "on"); -	v4l2_info(sd, "Connector type: %s\n", state->connector_hdmi ? -			"HDMI" : (is_digital_input(sd) ? "DVI-D" : "DVI-A"));  	v4l2_info(sd, "HDMI/DVI-D port selected: %s\n",  			state->hdmi_port_a ? "A" : "B");  	v4l2_info(sd, "EDID A %s, B %s\n", @@ -2360,15 +2524,63 @@ static int adv7842_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)  	return 0;  } +static void adv7842_s_sdp_io(struct v4l2_subdev *sd, struct adv7842_sdp_io_sync_adjustment *s) +{ +	if (s && s->adjust) { +		sdp_io_write(sd, 0x94, (s->hs_beg >> 8) & 0xf); +		sdp_io_write(sd, 0x95, s->hs_beg & 0xff); +		sdp_io_write(sd, 0x96, (s->hs_width >> 8) & 0xf); +		sdp_io_write(sd, 0x97, s->hs_width & 0xff); +		sdp_io_write(sd, 0x98, (s->de_beg >> 8) & 0xf); +		sdp_io_write(sd, 0x99, s->de_beg & 0xff); +		sdp_io_write(sd, 0x9a, (s->de_end >> 8) & 0xf); +		sdp_io_write(sd, 0x9b, s->de_end & 0xff); +		sdp_io_write(sd, 0xa8, s->vs_beg_o); +		sdp_io_write(sd, 0xa9, s->vs_beg_e); +		sdp_io_write(sd, 0xaa, s->vs_end_o); +		sdp_io_write(sd, 0xab, s->vs_end_e); +		sdp_io_write(sd, 0xac, s->de_v_beg_o); +		sdp_io_write(sd, 0xad, s->de_v_beg_e); +		sdp_io_write(sd, 0xae, s->de_v_end_o); +		sdp_io_write(sd, 0xaf, s->de_v_end_e); +	} else { +		/* set to default */ +		sdp_io_write(sd, 0x94, 0x00); +		sdp_io_write(sd, 0x95, 0x00); +		sdp_io_write(sd, 0x96, 0x00); +		sdp_io_write(sd, 0x97, 0x20); +		sdp_io_write(sd, 0x98, 0x00); +		sdp_io_write(sd, 0x99, 0x00); +		sdp_io_write(sd, 0x9a, 0x00); +		sdp_io_write(sd, 0x9b, 0x00); +		sdp_io_write(sd, 0xa8, 0x04); +		sdp_io_write(sd, 0xa9, 0x04); +		sdp_io_write(sd, 0xaa, 0x04); +		sdp_io_write(sd, 0xab, 0x04); +		sdp_io_write(sd, 0xac, 0x04); +		sdp_io_write(sd, 0xad, 0x04); +		sdp_io_write(sd, 0xae, 0x04); +		sdp_io_write(sd, 0xaf, 0x04); +	} +} +  static int adv7842_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)  {  	struct adv7842_state *state = to_state(sd); +	struct adv7842_platform_data *pdata = &state->pdata;  	v4l2_dbg(1, debug, sd, "%s:\n", __func__);  	if (state->mode != ADV7842_MODE_SDP)  		return -ENODATA; +	if (norm & V4L2_STD_625_50) +		adv7842_s_sdp_io(sd, &pdata->sdp_io_sync_625); +	else if (norm & V4L2_STD_525_60) +		adv7842_s_sdp_io(sd, &pdata->sdp_io_sync_525); +	else +		adv7842_s_sdp_io(sd, NULL); +  	if (norm & V4L2_STD_ALL) {  		state->norm = norm;  		return 0; @@ -2391,22 +2603,29 @@ static int adv7842_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm)  /* ----------------------------------------------------------------------- */ -static int adv7842_core_init(struct v4l2_subdev *sd, -		const struct adv7842_platform_data *pdata) +static int adv7842_core_init(struct v4l2_subdev *sd)  { +	struct adv7842_state *state = to_state(sd); +	struct adv7842_platform_data *pdata = &state->pdata;  	hdmi_write(sd, 0x48,  		   (pdata->disable_pwrdnb ? 0x80 : 0) |  		   (pdata->disable_cable_det_rst ? 0x40 : 0));  	disable_input(sd); +	/* +	 * Disable I2C access to internal EDID ram from HDMI DDC ports +	 * Disable auto edid enable when leaving powerdown mode +	 */ +	rep_write_and_or(sd, 0x77, 0xd3, 0x20); +  	/* power */  	io_write(sd, 0x0c, 0x42);   /* Power up part and power down VDP */  	io_write(sd, 0x15, 0x80);   /* Power up pads */  	/* video format */  	io_write(sd, 0x02, -		 pdata->inp_color_space << 4 | +		 0xf0 |  		 pdata->alt_gamma << 3 |  		 pdata->op_656_range << 2 |  		 pdata->rgb_out << 1 | @@ -2418,13 +2637,24 @@ static int adv7842_core_init(struct v4l2_subdev *sd,  			pdata->replicate_av_codes << 1 |  			pdata->invert_cbcr << 0); +	/* HDMI audio */ +	hdmi_write_and_or(sd, 0x1a, 0xf1, 0x08); /* Wait 1 s before unmute */ +  	/* Drive strength */ -	io_write_and_or(sd, 0x14, 0xc0, pdata->drive_strength.data<<4 | -			pdata->drive_strength.clock<<2 | -			pdata->drive_strength.sync); +	io_write_and_or(sd, 0x14, 0xc0, +			pdata->dr_str_data << 4 | +			pdata->dr_str_clk << 2 | +			pdata->dr_str_sync);  	/* HDMI free run */ -	cp_write(sd, 0xba, (pdata->hdmi_free_run_mode << 1) | 0x01); +	cp_write_and_or(sd, 0xba, 0xfc, pdata->hdmi_free_run_enable | +					(pdata->hdmi_free_run_mode << 1)); + +	/* SPD free run */ +	sdp_write_and_or(sd, 0xdd, 0xf0, pdata->sdp_free_run_force | +					 (pdata->sdp_free_run_cbar_en << 1) | +					 (pdata->sdp_free_run_man_col_en << 2) | +					 (pdata->sdp_free_run_auto << 3));  	/* TODO from platform data */  	cp_write(sd, 0x69, 0x14);   /* Enable CP CSC */ @@ -2437,18 +2667,6 @@ static int adv7842_core_init(struct v4l2_subdev *sd,  	sdp_csc_coeff(sd, &pdata->sdp_csc_coeff); -	if (pdata->sdp_io_sync.adjust) { -		const struct adv7842_sdp_io_sync_adjustment *s = &pdata->sdp_io_sync; -		sdp_io_write(sd, 0x94, (s->hs_beg>>8) & 0xf); -		sdp_io_write(sd, 0x95, s->hs_beg & 0xff); -		sdp_io_write(sd, 0x96, (s->hs_width>>8) & 0xf); -		sdp_io_write(sd, 0x97, s->hs_width & 0xff); -		sdp_io_write(sd, 0x98, (s->de_beg>>8) & 0xf); -		sdp_io_write(sd, 0x99, s->de_beg & 0xff); -		sdp_io_write(sd, 0x9a, (s->de_end>>8) & 0xf); -		sdp_io_write(sd, 0x9b, s->de_end & 0xff); -	} -  	/* todo, improve settings for sdram */  	if (pdata->sd_ram_size >= 128) {  		sdp_write(sd, 0x12, 0x0d); /* Frame TBC,3D comb enabled */ @@ -2481,20 +2699,22 @@ static int adv7842_core_init(struct v4l2_subdev *sd,  	enable_input(sd); -	/* disable I2C access to internal EDID ram from HDMI DDC ports */ -	rep_write_and_or(sd, 0x77, 0xf3, 0x00); - -	hdmi_write(sd, 0x69, 0xa3); /* HPA manual */ -	/* HPA disable on port A and B */ -	io_write_and_or(sd, 0x20, 0xcf, 0x00); +	if (pdata->hpa_auto) { +		/* HPA auto, HPA 0.5s after Edid set and Cable detect */ +		hdmi_write(sd, 0x69, 0x5c); +	} else { +		/* HPA manual */ +		hdmi_write(sd, 0x69, 0xa3); +		/* HPA disable on port A and B */ +		io_write_and_or(sd, 0x20, 0xcf, 0x00); +	}  	/* LLC */ -	/* Set phase to 16. TODO: get this from platform_data */ -	io_write(sd, 0x19, 0x90); +	io_write(sd, 0x19, 0x80 | pdata->llc_dll_phase);  	io_write(sd, 0x33, 0x40);  	/* interrupts */ -	io_write(sd, 0x40, 0xe2); /* Configure INT1 */ +	io_write(sd, 0x40, 0xf2); /* Configure INT1 */  	adv7842_irq_enable(sd, true); @@ -2594,6 +2814,7 @@ static int adv7842_command_ram_test(struct v4l2_subdev *sd)  	struct i2c_client *client = v4l2_get_subdevdata(sd);  	struct adv7842_state *state = to_state(sd);  	struct adv7842_platform_data *pdata = client->dev.platform_data; +	struct v4l2_dv_timings timings;  	int ret = 0;  	if (!pdata) @@ -2616,7 +2837,7 @@ static int adv7842_command_ram_test(struct v4l2_subdev *sd)  	adv7842_rewrite_i2c_addresses(sd, pdata);  	/* and re-init chip and state */ -	adv7842_core_init(sd, pdata); +	adv7842_core_init(sd);  	disable_input(sd); @@ -2624,11 +2845,15 @@ static int adv7842_command_ram_test(struct v4l2_subdev *sd)  	enable_input(sd); -	adv7842_s_dv_timings(sd, &state->timings); -  	edid_write_vga_segment(sd); -	edid_write_hdmi_segment(sd, 0); -	edid_write_hdmi_segment(sd, 1); +	edid_write_hdmi_segment(sd, ADV7842_EDID_PORT_A); +	edid_write_hdmi_segment(sd, ADV7842_EDID_PORT_B); + +	timings = state->timings; + +	memset(&state->timings, 0, sizeof(struct v4l2_dv_timings)); + +	adv7842_s_dv_timings(sd, &timings);  	return ret;  } @@ -2650,8 +2875,6 @@ static const struct v4l2_ctrl_ops adv7842_ctrl_ops = {  static const struct v4l2_subdev_core_ops adv7842_core_ops = {  	.log_status = adv7842_log_status, -	.g_std = adv7842_g_std, -	.s_std = adv7842_s_std,  	.ioctl = adv7842_ioctl,  	.interrupt_service_routine = adv7842_isr,  #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -2661,14 +2884,14 @@ static const struct v4l2_subdev_core_ops adv7842_core_ops = {  };  static const struct v4l2_subdev_video_ops adv7842_video_ops = { +	.g_std = adv7842_g_std, +	.s_std = adv7842_s_std,  	.s_routing = adv7842_s_routing,  	.querystd = adv7842_querystd,  	.g_input_status = adv7842_g_input_status,  	.s_dv_timings = adv7842_s_dv_timings,  	.g_dv_timings = adv7842_g_dv_timings,  	.query_dv_timings = adv7842_query_dv_timings, -	.enum_dv_timings = adv7842_enum_dv_timings, -	.dv_timings_cap = adv7842_dv_timings_cap,  	.enum_mbus_fmt = adv7842_enum_mbus_fmt,  	.g_mbus_fmt = adv7842_g_mbus_fmt,  	.try_mbus_fmt = adv7842_g_mbus_fmt, @@ -2676,7 +2899,10 @@ static const struct v4l2_subdev_video_ops adv7842_video_ops = {  };  static const struct v4l2_subdev_pad_ops adv7842_pad_ops = { +	.get_edid = adv7842_get_edid,  	.set_edid = adv7842_set_edid, +	.enum_dv_timings = adv7842_enum_dv_timings, +	.dv_timings_cap = adv7842_dv_timings_cap,  };  static const struct v4l2_subdev_ops adv7842_ops = { @@ -2718,8 +2944,9 @@ static const struct v4l2_ctrl_config adv7842_ctrl_free_run_color = {  }; -static void adv7842_unregister_clients(struct adv7842_state *state) +static void adv7842_unregister_clients(struct v4l2_subdev *sd)  { +	struct adv7842_state *state = to_state(sd);  	if (state->i2c_avlink)  		i2c_unregister_device(state->i2c_avlink);  	if (state->i2c_cec) @@ -2742,21 +2969,79 @@ static void adv7842_unregister_clients(struct adv7842_state *state)  		i2c_unregister_device(state->i2c_cp);  	if (state->i2c_vdp)  		i2c_unregister_device(state->i2c_vdp); + +	state->i2c_avlink = NULL; +	state->i2c_cec = NULL; +	state->i2c_infoframe = NULL; +	state->i2c_sdp_io = NULL; +	state->i2c_sdp = NULL; +	state->i2c_afe = NULL; +	state->i2c_repeater = NULL; +	state->i2c_edid = NULL; +	state->i2c_hdmi = NULL; +	state->i2c_cp = NULL; +	state->i2c_vdp = NULL;  } -static struct i2c_client *adv7842_dummy_client(struct v4l2_subdev *sd, +static struct i2c_client *adv7842_dummy_client(struct v4l2_subdev *sd, const char *desc,  					       u8 addr, u8 io_reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); +	struct i2c_client *cp;  	io_write(sd, io_reg, addr << 1); -	return i2c_new_dummy(client->adapter, io_read(sd, io_reg) >> 1); + +	if (addr == 0) { +		v4l2_err(sd, "no %s i2c addr configured\n", desc); +		return NULL; +	} + +	cp = i2c_new_dummy(client->adapter, io_read(sd, io_reg) >> 1); +	if (!cp) +		v4l2_err(sd, "register %s on i2c addr 0x%x failed\n", desc, addr); + +	return cp; +} + +static int adv7842_register_clients(struct v4l2_subdev *sd) +{ +	struct adv7842_state *state = to_state(sd); +	struct adv7842_platform_data *pdata = &state->pdata; + +	state->i2c_avlink = adv7842_dummy_client(sd, "avlink", pdata->i2c_avlink, 0xf3); +	state->i2c_cec = adv7842_dummy_client(sd, "cec", pdata->i2c_cec, 0xf4); +	state->i2c_infoframe = adv7842_dummy_client(sd, "infoframe", pdata->i2c_infoframe, 0xf5); +	state->i2c_sdp_io = adv7842_dummy_client(sd, "sdp_io", pdata->i2c_sdp_io, 0xf2); +	state->i2c_sdp = adv7842_dummy_client(sd, "sdp", pdata->i2c_sdp, 0xf1); +	state->i2c_afe = adv7842_dummy_client(sd, "afe", pdata->i2c_afe, 0xf8); +	state->i2c_repeater = adv7842_dummy_client(sd, "repeater", pdata->i2c_repeater, 0xf9); +	state->i2c_edid = adv7842_dummy_client(sd, "edid", pdata->i2c_edid, 0xfa); +	state->i2c_hdmi = adv7842_dummy_client(sd, "hdmi", pdata->i2c_hdmi, 0xfb); +	state->i2c_cp = adv7842_dummy_client(sd, "cp", pdata->i2c_cp, 0xfd); +	state->i2c_vdp = adv7842_dummy_client(sd, "vdp", pdata->i2c_vdp, 0xfe); + +	if (!state->i2c_avlink || +	    !state->i2c_cec || +	    !state->i2c_infoframe || +	    !state->i2c_sdp_io || +	    !state->i2c_sdp || +	    !state->i2c_afe || +	    !state->i2c_repeater || +	    !state->i2c_edid || +	    !state->i2c_hdmi || +	    !state->i2c_cp || +	    !state->i2c_vdp) +		return -1; + +	return 0;  }  static int adv7842_probe(struct i2c_client *client,  			 const struct i2c_device_id *id)  {  	struct adv7842_state *state; +	static const struct v4l2_dv_timings cea640x480 = +		V4L2_DV_BT_CEA_640X480P59_94;  	struct adv7842_platform_data *pdata = client->dev.platform_data;  	struct v4l2_ctrl_handler *hdl;  	struct v4l2_subdev *sd; @@ -2781,13 +3066,17 @@ static int adv7842_probe(struct i2c_client *client,  		return -ENOMEM;  	} +	/* platform data */ +	state->pdata = *pdata; +	state->timings = cea640x480; +  	sd = &state->sd;  	v4l2_i2c_subdev_init(sd, client, &adv7842_ops);  	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; -	state->connector_hdmi = pdata->connector_hdmi;  	state->mode = pdata->mode; -	state->hdmi_port_a = true; +	state->hdmi_port_a = pdata->input == ADV7842_SELECT_HDMI_PORT_A; +	state->restart_stdi_once = true;  	/* i2c access to adv7842? */  	rev = adv_smbus_read_byte_data_check(client, 0xea, false) << 8 | @@ -2849,21 +3138,7 @@ static int adv7842_probe(struct i2c_client *client,  		goto err_hdl;  	} -	state->i2c_avlink = adv7842_dummy_client(sd, pdata->i2c_avlink, 0xf3); -	state->i2c_cec = adv7842_dummy_client(sd, pdata->i2c_cec, 0xf4); -	state->i2c_infoframe = adv7842_dummy_client(sd, pdata->i2c_infoframe, 0xf5); -	state->i2c_sdp_io = adv7842_dummy_client(sd, pdata->i2c_sdp_io, 0xf2); -	state->i2c_sdp = adv7842_dummy_client(sd, pdata->i2c_sdp, 0xf1); -	state->i2c_afe = adv7842_dummy_client(sd, pdata->i2c_afe, 0xf8); -	state->i2c_repeater = adv7842_dummy_client(sd, pdata->i2c_repeater, 0xf9); -	state->i2c_edid = adv7842_dummy_client(sd, pdata->i2c_edid, 0xfa); -	state->i2c_hdmi = adv7842_dummy_client(sd, pdata->i2c_hdmi, 0xfb); -	state->i2c_cp = adv7842_dummy_client(sd, pdata->i2c_cp, 0xfd); -	state->i2c_vdp = adv7842_dummy_client(sd, pdata->i2c_vdp, 0xfe); -	if (!state->i2c_avlink || !state->i2c_cec || !state->i2c_infoframe || -	    !state->i2c_sdp_io || !state->i2c_sdp || !state->i2c_afe || -	    !state->i2c_repeater || !state->i2c_edid || !state->i2c_hdmi || -	    !state->i2c_cp || !state->i2c_vdp) { +	if (adv7842_register_clients(sd) < 0) {  		err = -ENOMEM;  		v4l2_err(sd, "failed to create all i2c clients\n");  		goto err_i2c; @@ -2885,7 +3160,7 @@ static int adv7842_probe(struct i2c_client *client,  	if (err)  		goto err_work_queues; -	err = adv7842_core_init(sd, pdata); +	err = adv7842_core_init(sd);  	if (err)  		goto err_entity; @@ -2899,7 +3174,7 @@ err_work_queues:  	cancel_delayed_work(&state->delayed_work_enable_hotplug);  	destroy_workqueue(state->work_queues);  err_i2c: -	adv7842_unregister_clients(state); +	adv7842_unregister_clients(sd);  err_hdl:  	v4l2_ctrl_handler_free(hdl);  	return err; @@ -2918,7 +3193,7 @@ static int adv7842_remove(struct i2c_client *client)  	destroy_workqueue(state->work_queues);  	v4l2_device_unregister_subdev(sd);  	media_entity_cleanup(&sd->entity); -	adv7842_unregister_clients(to_state(sd)); +	adv7842_unregister_clients(sd);  	v4l2_ctrl_handler_free(sd->ctrl_handler);  	return 0;  } diff --git a/drivers/media/i2c/bt819.c b/drivers/media/i2c/bt819.c index 369cf6ff88f..76b334a6a56 100644 --- a/drivers/media/i2c/bt819.c +++ b/drivers/media/i2c/bt819.c @@ -387,10 +387,10 @@ static const struct v4l2_subdev_core_ops bt819_core_ops = {  	.s_ctrl = v4l2_subdev_s_ctrl,  	.queryctrl = v4l2_subdev_queryctrl,  	.querymenu = v4l2_subdev_querymenu, -	.s_std = bt819_s_std,  };  static const struct v4l2_subdev_video_ops bt819_video_ops = { +	.s_std = bt819_s_std,  	.s_routing = bt819_s_routing,  	.s_stream = bt819_s_stream,  	.querystd = bt819_querystd, diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c index 2e3771d5735..e453a3ffe7d 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.c +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -5041,8 +5041,6 @@ static const struct v4l2_subdev_core_ops cx25840_core_ops = {  	.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,  	.queryctrl = v4l2_subdev_queryctrl,  	.querymenu = v4l2_subdev_querymenu, -	.s_std = cx25840_s_std, -	.g_std = cx25840_g_std,  	.reset = cx25840_reset,  	.load_fw = cx25840_load_fw,  	.s_io_pin_config = common_s_io_pin_config, @@ -5067,6 +5065,8 @@ static const struct v4l2_subdev_audio_ops cx25840_audio_ops = {  };  static const struct v4l2_subdev_video_ops cx25840_video_ops = { +	.s_std = cx25840_s_std, +	.g_std = cx25840_g_std,  	.s_routing = cx25840_s_video_routing,  	.s_mbus_fmt = cx25840_s_mbus_fmt,  	.s_stream = cx25840_s_stream, diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c index 82bf5679da3..c8fe1358ec9 100644 --- a/drivers/media/i2c/ir-kbd-i2c.c +++ b/drivers/media/i2c/ir-kbd-i2c.c @@ -394,7 +394,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)  	if (!rc) {  		/* -		 * If platform_data doesn't specify rc_dev, initilize it +		 * If platform_data doesn't specify rc_dev, initialize it  		 * internally  		 */  		rc = rc_allocate_device(); @@ -431,8 +431,8 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)  	 * Initialize the other fields of rc_dev  	 */  	rc->map_name       = ir->ir_codes; -	rc->allowed_protos = rc_type; -	rc->enabled_protocols = rc_type; +	rc_set_allowed_protocols(rc, rc_type); +	rc_set_enabled_protocols(rc, rc_type);  	if (!rc->driver_name)  		rc->driver_name = MODULE_NAME; diff --git a/drivers/media/i2c/ks0127.c b/drivers/media/i2c/ks0127.c index c3e94ae82c0..25b81bc58c8 100644 --- a/drivers/media/i2c/ks0127.c +++ b/drivers/media/i2c/ks0127.c @@ -648,11 +648,8 @@ static int ks0127_g_input_status(struct v4l2_subdev *sd, u32 *status)  /* ----------------------------------------------------------------------- */ -static const struct v4l2_subdev_core_ops ks0127_core_ops = { -	.s_std = ks0127_s_std, -}; -  static const struct v4l2_subdev_video_ops ks0127_video_ops = { +	.s_std = ks0127_s_std,  	.s_routing = ks0127_s_routing,  	.s_stream = ks0127_s_stream,  	.querystd = ks0127_querystd, @@ -660,7 +657,6 @@ static const struct v4l2_subdev_video_ops ks0127_video_ops = {  };  static const struct v4l2_subdev_ops ks0127_ops = { -	.core = &ks0127_core_ops,  	.video = &ks0127_video_ops,  }; diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c new file mode 100644 index 00000000000..c23de593c17 --- /dev/null +++ b/drivers/media/i2c/lm3560.c @@ -0,0 +1,488 @@ +/* + * drivers/media/i2c/lm3560.c + * General device driver for TI lm3560, FLASH LED Driver + * + * Copyright (C) 2013 Texas Instruments + * + * Contact: Daniel Jeong <gshark.jeong@gmail.com> + *			Ldd-Mlp <ldd-mlp@list.ti.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + */ + +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/mutex.h> +#include <linux/regmap.h> +#include <linux/videodev2.h> +#include <media/lm3560.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> + +/* registers definitions */ +#define REG_ENABLE		0x10 +#define REG_TORCH_BR	0xa0 +#define REG_FLASH_BR	0xb0 +#define REG_FLASH_TOUT	0xc0 +#define REG_FLAG		0xd0 +#define REG_CONFIG1		0xe0 + +/* fault mask */ +#define FAULT_TIMEOUT	(1<<0) +#define FAULT_OVERTEMP	(1<<1) +#define FAULT_SHORT_CIRCUIT	(1<<2) + +enum led_enable { +	MODE_SHDN = 0x0, +	MODE_TORCH = 0x2, +	MODE_FLASH = 0x3, +}; + +/** + * struct lm3560_flash + * + * @pdata: platform data + * @regmap: reg. map for i2c + * @lock: muxtex for serial access. + * @led_mode: V4L2 LED mode + * @ctrls_led: V4L2 contols + * @subdev_led: V4L2 subdev + */ +struct lm3560_flash { +	struct device *dev; +	struct lm3560_platform_data *pdata; +	struct regmap *regmap; +	struct mutex lock; + +	enum v4l2_flash_led_mode led_mode; +	struct v4l2_ctrl_handler ctrls_led[LM3560_LED_MAX]; +	struct v4l2_subdev subdev_led[LM3560_LED_MAX]; +}; + +#define to_lm3560_flash(_ctrl, _no)	\ +	container_of(_ctrl->handler, struct lm3560_flash, ctrls_led[_no]) + +/* enable mode control */ +static int lm3560_mode_ctrl(struct lm3560_flash *flash) +{ +	int rval = -EINVAL; + +	switch (flash->led_mode) { +	case V4L2_FLASH_LED_MODE_NONE: +		rval = regmap_update_bits(flash->regmap, +					  REG_ENABLE, 0x03, MODE_SHDN); +		break; +	case V4L2_FLASH_LED_MODE_TORCH: +		rval = regmap_update_bits(flash->regmap, +					  REG_ENABLE, 0x03, MODE_TORCH); +		break; +	case V4L2_FLASH_LED_MODE_FLASH: +		rval = regmap_update_bits(flash->regmap, +					  REG_ENABLE, 0x03, MODE_FLASH); +		break; +	} +	return rval; +} + +/* led1/2 enable/disable */ +static int lm3560_enable_ctrl(struct lm3560_flash *flash, +			      enum lm3560_led_id led_no, bool on) +{ +	int rval; + +	if (led_no == LM3560_LED0) { +		if (on == true) +			rval = regmap_update_bits(flash->regmap, +						  REG_ENABLE, 0x08, 0x08); +		else +			rval = regmap_update_bits(flash->regmap, +						  REG_ENABLE, 0x08, 0x00); +	} else { +		if (on == true) +			rval = regmap_update_bits(flash->regmap, +						  REG_ENABLE, 0x10, 0x10); +		else +			rval = regmap_update_bits(flash->regmap, +						  REG_ENABLE, 0x10, 0x00); +	} +	return rval; +} + +/* torch1/2 brightness control */ +static int lm3560_torch_brt_ctrl(struct lm3560_flash *flash, +				 enum lm3560_led_id led_no, unsigned int brt) +{ +	int rval; +	u8 br_bits; + +	if (brt < LM3560_TORCH_BRT_MIN) +		return lm3560_enable_ctrl(flash, led_no, false); +	else +		rval = lm3560_enable_ctrl(flash, led_no, true); + +	br_bits = LM3560_TORCH_BRT_uA_TO_REG(brt); +	if (led_no == LM3560_LED0) +		rval = regmap_update_bits(flash->regmap, +					  REG_TORCH_BR, 0x07, br_bits); +	else +		rval = regmap_update_bits(flash->regmap, +					  REG_TORCH_BR, 0x38, br_bits << 3); + +	return rval; +} + +/* flash1/2 brightness control */ +static int lm3560_flash_brt_ctrl(struct lm3560_flash *flash, +				 enum lm3560_led_id led_no, unsigned int brt) +{ +	int rval; +	u8 br_bits; + +	if (brt < LM3560_FLASH_BRT_MIN) +		return lm3560_enable_ctrl(flash, led_no, false); +	else +		rval = lm3560_enable_ctrl(flash, led_no, true); + +	br_bits = LM3560_FLASH_BRT_uA_TO_REG(brt); +	if (led_no == LM3560_LED0) +		rval = regmap_update_bits(flash->regmap, +					  REG_FLASH_BR, 0x0f, br_bits); +	else +		rval = regmap_update_bits(flash->regmap, +					  REG_FLASH_BR, 0xf0, br_bits << 4); + +	return rval; +} + +/* v4l2 controls  */ +static int lm3560_get_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) +{ +	struct lm3560_flash *flash = to_lm3560_flash(ctrl, led_no); +	int rval = -EINVAL; + +	mutex_lock(&flash->lock); + +	if (ctrl->id == V4L2_CID_FLASH_FAULT) { +		s32 fault = 0; +		unsigned int reg_val; +		rval = regmap_read(flash->regmap, REG_FLAG, ®_val); +		if (rval < 0) +			goto out; +		if (reg_val & FAULT_SHORT_CIRCUIT) +			fault |= V4L2_FLASH_FAULT_SHORT_CIRCUIT; +		if (reg_val & FAULT_OVERTEMP) +			fault |= V4L2_FLASH_FAULT_OVER_TEMPERATURE; +		if (reg_val & FAULT_TIMEOUT) +			fault |= V4L2_FLASH_FAULT_TIMEOUT; +		ctrl->cur.val = fault; +	} + +out: +	mutex_unlock(&flash->lock); +	return rval; +} + +static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no) +{ +	struct lm3560_flash *flash = to_lm3560_flash(ctrl, led_no); +	u8 tout_bits; +	int rval = -EINVAL; + +	mutex_lock(&flash->lock); + +	switch (ctrl->id) { +	case V4L2_CID_FLASH_LED_MODE: +		flash->led_mode = ctrl->val; +		if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) +			rval = lm3560_mode_ctrl(flash); +		break; + +	case V4L2_CID_FLASH_STROBE_SOURCE: +		rval = regmap_update_bits(flash->regmap, +					  REG_CONFIG1, 0x04, (ctrl->val) << 2); +		if (rval < 0) +			goto err_out; +		break; + +	case V4L2_CID_FLASH_STROBE: +		if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) { +			rval = -EBUSY; +			goto err_out; +		} +		flash->led_mode = V4L2_FLASH_LED_MODE_FLASH; +		rval = lm3560_mode_ctrl(flash); +		break; + +	case V4L2_CID_FLASH_STROBE_STOP: +		if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) { +			rval = -EBUSY; +			goto err_out; +		} +		flash->led_mode = V4L2_FLASH_LED_MODE_NONE; +		rval = lm3560_mode_ctrl(flash); +		break; + +	case V4L2_CID_FLASH_TIMEOUT: +		tout_bits = LM3560_FLASH_TOUT_ms_TO_REG(ctrl->val); +		rval = regmap_update_bits(flash->regmap, +					  REG_FLASH_TOUT, 0x1f, tout_bits); +		break; + +	case V4L2_CID_FLASH_INTENSITY: +		rval = lm3560_flash_brt_ctrl(flash, led_no, ctrl->val); +		break; + +	case V4L2_CID_FLASH_TORCH_INTENSITY: +		rval = lm3560_torch_brt_ctrl(flash, led_no, ctrl->val); +		break; +	} + +err_out: +	mutex_unlock(&flash->lock); +	return rval; +} + +static int lm3560_led1_get_ctrl(struct v4l2_ctrl *ctrl) +{ +	return lm3560_get_ctrl(ctrl, LM3560_LED1); +} + +static int lm3560_led1_set_ctrl(struct v4l2_ctrl *ctrl) +{ +	return lm3560_set_ctrl(ctrl, LM3560_LED1); +} + +static int lm3560_led0_get_ctrl(struct v4l2_ctrl *ctrl) +{ +	return lm3560_get_ctrl(ctrl, LM3560_LED0); +} + +static int lm3560_led0_set_ctrl(struct v4l2_ctrl *ctrl) +{ +	return lm3560_set_ctrl(ctrl, LM3560_LED0); +} + +static const struct v4l2_ctrl_ops lm3560_led_ctrl_ops[LM3560_LED_MAX] = { +	[LM3560_LED0] = { +			 .g_volatile_ctrl = lm3560_led0_get_ctrl, +			 .s_ctrl = lm3560_led0_set_ctrl, +			 }, +	[LM3560_LED1] = { +			 .g_volatile_ctrl = lm3560_led1_get_ctrl, +			 .s_ctrl = lm3560_led1_set_ctrl, +			 } +}; + +static int lm3560_init_controls(struct lm3560_flash *flash, +				enum lm3560_led_id led_no) +{ +	struct v4l2_ctrl *fault; +	u32 max_flash_brt = flash->pdata->max_flash_brt[led_no]; +	u32 max_torch_brt = flash->pdata->max_torch_brt[led_no]; +	struct v4l2_ctrl_handler *hdl = &flash->ctrls_led[led_no]; +	const struct v4l2_ctrl_ops *ops = &lm3560_led_ctrl_ops[led_no]; + +	v4l2_ctrl_handler_init(hdl, 8); + +	/* flash mode */ +	v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_LED_MODE, +			       V4L2_FLASH_LED_MODE_TORCH, ~0x7, +			       V4L2_FLASH_LED_MODE_NONE); +	flash->led_mode = V4L2_FLASH_LED_MODE_NONE; + +	/* flash source */ +	v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_STROBE_SOURCE, +			       0x1, ~0x3, V4L2_FLASH_STROBE_SOURCE_SOFTWARE); + +	/* flash strobe */ +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_STROBE, 0, 0, 0, 0); + +	/* flash strobe stop */ +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0); + +	/* flash strobe timeout */ +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TIMEOUT, +			  LM3560_FLASH_TOUT_MIN, +			  flash->pdata->max_flash_timeout, +			  LM3560_FLASH_TOUT_STEP, +			  flash->pdata->max_flash_timeout); + +	/* flash brt */ +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_INTENSITY, +			  LM3560_FLASH_BRT_MIN, max_flash_brt, +			  LM3560_FLASH_BRT_STEP, max_flash_brt); + +	/* torch brt */ +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TORCH_INTENSITY, +			  LM3560_TORCH_BRT_MIN, max_torch_brt, +			  LM3560_TORCH_BRT_STEP, max_torch_brt); + +	/* fault */ +	fault = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_FAULT, 0, +				  V4L2_FLASH_FAULT_OVER_VOLTAGE +				  | V4L2_FLASH_FAULT_OVER_TEMPERATURE +				  | V4L2_FLASH_FAULT_SHORT_CIRCUIT +				  | V4L2_FLASH_FAULT_TIMEOUT, 0, 0); +	if (fault != NULL) +		fault->flags |= V4L2_CTRL_FLAG_VOLATILE; + +	if (hdl->error) +		return hdl->error; + +	flash->subdev_led[led_no].ctrl_handler = hdl; +	return 0; +} + +/* initialize device */ +static const struct v4l2_subdev_ops lm3560_ops = { +	.core = NULL, +}; + +static const struct regmap_config lm3560_regmap = { +	.reg_bits = 8, +	.val_bits = 8, +	.max_register = 0xFF, +}; + +static int lm3560_subdev_init(struct lm3560_flash *flash, +			      enum lm3560_led_id led_no, char *led_name) +{ +	struct i2c_client *client = to_i2c_client(flash->dev); +	int rval; + +	v4l2_i2c_subdev_init(&flash->subdev_led[led_no], client, &lm3560_ops); +	flash->subdev_led[led_no].flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; +	strcpy(flash->subdev_led[led_no].name, led_name); +	rval = lm3560_init_controls(flash, led_no); +	if (rval) +		goto err_out; +	rval = media_entity_init(&flash->subdev_led[led_no].entity, 0, NULL, 0); +	if (rval < 0) +		goto err_out; +	flash->subdev_led[led_no].entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; + +	return rval; + +err_out: +	v4l2_ctrl_handler_free(&flash->ctrls_led[led_no]); +	return rval; +} + +static int lm3560_init_device(struct lm3560_flash *flash) +{ +	int rval; +	unsigned int reg_val; + +	/* set peak current */ +	rval = regmap_update_bits(flash->regmap, +				  REG_FLASH_TOUT, 0x60, flash->pdata->peak); +	if (rval < 0) +		return rval; +	/* output disable */ +	flash->led_mode = V4L2_FLASH_LED_MODE_NONE; +	rval = lm3560_mode_ctrl(flash); +	if (rval < 0) +		return rval; +	/* reset faults */ +	rval = regmap_read(flash->regmap, REG_FLAG, ®_val); +	return rval; +} + +static int lm3560_probe(struct i2c_client *client, +			const struct i2c_device_id *devid) +{ +	struct lm3560_flash *flash; +	struct lm3560_platform_data *pdata = dev_get_platdata(&client->dev); +	int rval; + +	flash = devm_kzalloc(&client->dev, sizeof(*flash), GFP_KERNEL); +	if (flash == NULL) +		return -ENOMEM; + +	flash->regmap = devm_regmap_init_i2c(client, &lm3560_regmap); +	if (IS_ERR(flash->regmap)) { +		rval = PTR_ERR(flash->regmap); +		return rval; +	} + +	/* if there is no platform data, use chip default value */ +	if (pdata == NULL) { +		pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); +		if (pdata == NULL) +			return -ENODEV; +		pdata->peak = LM3560_PEAK_3600mA; +		pdata->max_flash_timeout = LM3560_FLASH_TOUT_MAX; +		/* led 1 */ +		pdata->max_flash_brt[LM3560_LED0] = LM3560_FLASH_BRT_MAX; +		pdata->max_torch_brt[LM3560_LED0] = LM3560_TORCH_BRT_MAX; +		/* led 2 */ +		pdata->max_flash_brt[LM3560_LED1] = LM3560_FLASH_BRT_MAX; +		pdata->max_torch_brt[LM3560_LED1] = LM3560_TORCH_BRT_MAX; +	} +	flash->pdata = pdata; +	flash->dev = &client->dev; +	mutex_init(&flash->lock); + +	rval = lm3560_subdev_init(flash, LM3560_LED0, "lm3560-led0"); +	if (rval < 0) +		return rval; + +	rval = lm3560_subdev_init(flash, LM3560_LED1, "lm3560-led1"); +	if (rval < 0) +		return rval; + +	rval = lm3560_init_device(flash); +	if (rval < 0) +		return rval; + +	i2c_set_clientdata(client, flash); + +	return 0; +} + +static int lm3560_remove(struct i2c_client *client) +{ +	struct lm3560_flash *flash = i2c_get_clientdata(client); +	unsigned int i; + +	for (i = LM3560_LED0; i < LM3560_LED_MAX; i++) { +		v4l2_device_unregister_subdev(&flash->subdev_led[i]); +		v4l2_ctrl_handler_free(&flash->ctrls_led[i]); +		media_entity_cleanup(&flash->subdev_led[i].entity); +	} + +	return 0; +} + +static const struct i2c_device_id lm3560_id_table[] = { +	{LM3560_NAME, 0}, +	{} +}; + +MODULE_DEVICE_TABLE(i2c, lm3560_id_table); + +static struct i2c_driver lm3560_i2c_driver = { +	.driver = { +		   .name = LM3560_NAME, +		   .pm = NULL, +		   }, +	.probe = lm3560_probe, +	.remove = lm3560_remove, +	.id_table = lm3560_id_table, +}; + +module_i2c_driver(lm3560_i2c_driver); + +MODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>"); +MODULE_AUTHOR("Ldd Mlp <ldd-mlp@list.ti.com>"); +MODULE_DESCRIPTION("Texas Instruments LM3560 LED flash driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/lm3646.c b/drivers/media/i2c/lm3646.c new file mode 100644 index 00000000000..626fb4679c0 --- /dev/null +++ b/drivers/media/i2c/lm3646.c @@ -0,0 +1,414 @@ +/* + * drivers/media/i2c/lm3646.c + * General device driver for TI lm3646, Dual FLASH LED Driver + * + * Copyright (C) 2014 Texas Instruments + * + * Contact: Daniel Jeong <gshark.jeong@gmail.com> + *			Ldd-Mlp <ldd-mlp@list.ti.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/regmap.h> +#include <linux/videodev2.h> +#include <media/lm3646.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> + +/* registers definitions */ +#define REG_ENABLE		0x01 +#define REG_TORCH_BR	0x05 +#define REG_FLASH_BR	0x05 +#define REG_FLASH_TOUT	0x04 +#define REG_FLAG		0x08 +#define REG_STROBE_SRC	0x06 +#define REG_LED1_FLASH_BR 0x06 +#define REG_LED1_TORCH_BR 0x07 + +#define MASK_ENABLE		0x03 +#define MASK_TORCH_BR	0x70 +#define MASK_FLASH_BR	0x0F +#define MASK_FLASH_TOUT	0x07 +#define MASK_FLAG		0xFF +#define MASK_STROBE_SRC	0x80 + +/* Fault Mask */ +#define FAULT_TIMEOUT	(1<<0) +#define FAULT_SHORT_CIRCUIT	(1<<1) +#define FAULT_UVLO		(1<<2) +#define FAULT_IVFM		(1<<3) +#define FAULT_OCP		(1<<4) +#define FAULT_OVERTEMP	(1<<5) +#define FAULT_NTC_TRIP	(1<<6) +#define FAULT_OVP		(1<<7) + +enum led_mode { +	MODE_SHDN = 0x0, +	MODE_TORCH = 0x2, +	MODE_FLASH = 0x3, +}; + +/* + * struct lm3646_flash + * + * @pdata: platform data + * @regmap: reg. map for i2c + * @lock: muxtex for serial access. + * @led_mode: V4L2 LED mode + * @ctrls_led: V4L2 contols + * @subdev_led: V4L2 subdev + * @mode_reg : mode register value + */ +struct lm3646_flash { +	struct device *dev; +	struct lm3646_platform_data *pdata; +	struct regmap *regmap; + +	struct v4l2_ctrl_handler ctrls_led; +	struct v4l2_subdev subdev_led; + +	u8 mode_reg; +}; + +#define to_lm3646_flash(_ctrl)	\ +	container_of(_ctrl->handler, struct lm3646_flash, ctrls_led) + +/* enable mode control */ +static int lm3646_mode_ctrl(struct lm3646_flash *flash, +			    enum v4l2_flash_led_mode led_mode) +{ +	switch (led_mode) { +	case V4L2_FLASH_LED_MODE_NONE: +		return regmap_write(flash->regmap, +				    REG_ENABLE, flash->mode_reg | MODE_SHDN); +	case V4L2_FLASH_LED_MODE_TORCH: +		return regmap_write(flash->regmap, +				    REG_ENABLE, flash->mode_reg | MODE_TORCH); +	case V4L2_FLASH_LED_MODE_FLASH: +		return regmap_write(flash->regmap, +				    REG_ENABLE, flash->mode_reg | MODE_FLASH); +	} +	return -EINVAL; +} + +/* V4L2 controls  */ +static int lm3646_get_ctrl(struct v4l2_ctrl *ctrl) +{ +	struct lm3646_flash *flash = to_lm3646_flash(ctrl); +	unsigned int reg_val; +	int rval; + +	if (ctrl->id != V4L2_CID_FLASH_FAULT) +		return -EINVAL; + +	rval = regmap_read(flash->regmap, REG_FLAG, ®_val); +	if (rval < 0) +		return rval; + +	ctrl->val = 0; +	if (reg_val & FAULT_TIMEOUT) +		ctrl->val |= V4L2_FLASH_FAULT_TIMEOUT; +	if (reg_val & FAULT_SHORT_CIRCUIT) +		ctrl->val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT; +	if (reg_val & FAULT_UVLO) +		ctrl->val |= V4L2_FLASH_FAULT_UNDER_VOLTAGE; +	if (reg_val & FAULT_IVFM) +		ctrl->val |= V4L2_FLASH_FAULT_INPUT_VOLTAGE; +	if (reg_val & FAULT_OCP) +		ctrl->val |= V4L2_FLASH_FAULT_OVER_CURRENT; +	if (reg_val & FAULT_OVERTEMP) +		ctrl->val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE; +	if (reg_val & FAULT_NTC_TRIP) +		ctrl->val |= V4L2_FLASH_FAULT_LED_OVER_TEMPERATURE; +	if (reg_val & FAULT_OVP) +		ctrl->val |= V4L2_FLASH_FAULT_OVER_VOLTAGE; + +	return 0; +} + +static int lm3646_set_ctrl(struct v4l2_ctrl *ctrl) +{ +	struct lm3646_flash *flash = to_lm3646_flash(ctrl); +	unsigned int reg_val; +	int rval = -EINVAL; + +	switch (ctrl->id) { +	case V4L2_CID_FLASH_LED_MODE: + +		if (ctrl->val != V4L2_FLASH_LED_MODE_FLASH) +			return lm3646_mode_ctrl(flash, ctrl->val); +		/* switch to SHDN mode before flash strobe on */ +		return lm3646_mode_ctrl(flash, V4L2_FLASH_LED_MODE_NONE); + +	case V4L2_CID_FLASH_STROBE_SOURCE: +		return regmap_update_bits(flash->regmap, +					  REG_STROBE_SRC, MASK_STROBE_SRC, +					  (ctrl->val) << 7); + +	case V4L2_CID_FLASH_STROBE: + +		/* read and check current mode of chip to start flash */ +		rval = regmap_read(flash->regmap, REG_ENABLE, ®_val); +		if (rval < 0 || ((reg_val & MASK_ENABLE) != MODE_SHDN)) +			return rval; +		/* flash on */ +		return lm3646_mode_ctrl(flash, V4L2_FLASH_LED_MODE_FLASH); + +	case V4L2_CID_FLASH_STROBE_STOP: + +		/* +		 * flash mode will be turned automatically +		 * from FLASH mode to SHDN mode after flash duration timeout +		 * read and check current mode of chip to stop flash +		 */ +		rval = regmap_read(flash->regmap, REG_ENABLE, ®_val); +		if (rval < 0) +			return rval; +		if ((reg_val & MASK_ENABLE) == MODE_FLASH) +			return lm3646_mode_ctrl(flash, +						V4L2_FLASH_LED_MODE_NONE); +		return rval; + +	case V4L2_CID_FLASH_TIMEOUT: +		return regmap_update_bits(flash->regmap, +					  REG_FLASH_TOUT, MASK_FLASH_TOUT, +					  LM3646_FLASH_TOUT_ms_TO_REG +					  (ctrl->val)); + +	case V4L2_CID_FLASH_INTENSITY: +		return regmap_update_bits(flash->regmap, +					  REG_FLASH_BR, MASK_FLASH_BR, +					  LM3646_TOTAL_FLASH_BRT_uA_TO_REG +					  (ctrl->val)); + +	case V4L2_CID_FLASH_TORCH_INTENSITY: +		return regmap_update_bits(flash->regmap, +					  REG_TORCH_BR, MASK_TORCH_BR, +					  LM3646_TOTAL_TORCH_BRT_uA_TO_REG +					  (ctrl->val) << 4); +	} + +	return -EINVAL; +} + +static const struct v4l2_ctrl_ops lm3646_led_ctrl_ops = { +	.g_volatile_ctrl = lm3646_get_ctrl, +	.s_ctrl = lm3646_set_ctrl, +}; + +static int lm3646_init_controls(struct lm3646_flash *flash) +{ +	struct v4l2_ctrl *fault; +	struct v4l2_ctrl_handler *hdl = &flash->ctrls_led; +	const struct v4l2_ctrl_ops *ops = &lm3646_led_ctrl_ops; + +	v4l2_ctrl_handler_init(hdl, 8); +	/* flash mode */ +	v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_LED_MODE, +			       V4L2_FLASH_LED_MODE_TORCH, ~0x7, +			       V4L2_FLASH_LED_MODE_NONE); + +	/* flash source */ +	v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_STROBE_SOURCE, +			       0x1, ~0x3, V4L2_FLASH_STROBE_SOURCE_SOFTWARE); + +	/* flash strobe */ +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_STROBE, 0, 0, 0, 0); +	/* flash strobe stop */ +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0); + +	/* flash strobe timeout */ +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TIMEOUT, +			  LM3646_FLASH_TOUT_MIN, +			  LM3646_FLASH_TOUT_MAX, +			  LM3646_FLASH_TOUT_STEP, flash->pdata->flash_timeout); + +	/* max flash current */ +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_INTENSITY, +			  LM3646_TOTAL_FLASH_BRT_MIN, +			  LM3646_TOTAL_FLASH_BRT_MAX, +			  LM3646_TOTAL_FLASH_BRT_STEP, +			  LM3646_TOTAL_FLASH_BRT_MAX); + +	/* max torch current */ +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TORCH_INTENSITY, +			  LM3646_TOTAL_TORCH_BRT_MIN, +			  LM3646_TOTAL_TORCH_BRT_MAX, +			  LM3646_TOTAL_TORCH_BRT_STEP, +			  LM3646_TOTAL_TORCH_BRT_MAX); + +	/* fault */ +	fault = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_FAULT, 0, +				  V4L2_FLASH_FAULT_OVER_VOLTAGE +				  | V4L2_FLASH_FAULT_OVER_TEMPERATURE +				  | V4L2_FLASH_FAULT_SHORT_CIRCUIT +				  | V4L2_FLASH_FAULT_TIMEOUT, 0, 0); +	if (fault != NULL) +		fault->flags |= V4L2_CTRL_FLAG_VOLATILE; + +	if (hdl->error) +		return hdl->error; + +	flash->subdev_led.ctrl_handler = hdl; +	return 0; +} + +/* initialize device */ +static const struct v4l2_subdev_ops lm3646_ops = { +	.core = NULL, +}; + +static const struct regmap_config lm3646_regmap = { +	.reg_bits = 8, +	.val_bits = 8, +	.max_register = 0xFF, +}; + +static int lm3646_subdev_init(struct lm3646_flash *flash) +{ +	struct i2c_client *client = to_i2c_client(flash->dev); +	int rval; + +	v4l2_i2c_subdev_init(&flash->subdev_led, client, &lm3646_ops); +	flash->subdev_led.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; +	strcpy(flash->subdev_led.name, LM3646_NAME); +	rval = lm3646_init_controls(flash); +	if (rval) +		goto err_out; +	rval = media_entity_init(&flash->subdev_led.entity, 0, NULL, 0); +	if (rval < 0) +		goto err_out; +	flash->subdev_led.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; +	return rval; + +err_out: +	v4l2_ctrl_handler_free(&flash->ctrls_led); +	return rval; +} + +static int lm3646_init_device(struct lm3646_flash *flash) +{ +	unsigned int reg_val; +	int rval; + +	/* read the value of mode register to reduce redundant i2c accesses */ +	rval = regmap_read(flash->regmap, REG_ENABLE, ®_val); +	if (rval < 0) +		return rval; +	flash->mode_reg = reg_val & 0xfc; + +	/* output disable */ +	rval = lm3646_mode_ctrl(flash, V4L2_FLASH_LED_MODE_NONE); +	if (rval < 0) +		return rval; + +	/* +	 * LED1 flash current setting +	 * LED2 flash current = Total(Max) flash current - LED1 flash current +	 */ +	rval = regmap_update_bits(flash->regmap, +				  REG_LED1_FLASH_BR, 0x7F, +				  LM3646_LED1_FLASH_BRT_uA_TO_REG +				  (flash->pdata->led1_flash_brt)); + +	if (rval < 0) +		return rval; + +	/* +	 * LED1 torch current setting +	 * LED2 torch current = Total(Max) torch current - LED1 torch current +	 */ +	rval = regmap_update_bits(flash->regmap, +				  REG_LED1_TORCH_BR, 0x7F, +				  LM3646_LED1_TORCH_BRT_uA_TO_REG +				  (flash->pdata->led1_torch_brt)); +	if (rval < 0) +		return rval; + +	/* Reset flag register */ +	return regmap_read(flash->regmap, REG_FLAG, ®_val); +} + +static int lm3646_probe(struct i2c_client *client, +			const struct i2c_device_id *devid) +{ +	struct lm3646_flash *flash; +	struct lm3646_platform_data *pdata = dev_get_platdata(&client->dev); +	int rval; + +	flash = devm_kzalloc(&client->dev, sizeof(*flash), GFP_KERNEL); +	if (flash == NULL) +		return -ENOMEM; + +	flash->regmap = devm_regmap_init_i2c(client, &lm3646_regmap); +	if (IS_ERR(flash->regmap)) +		return PTR_ERR(flash->regmap); + +	/* check device tree if there is no platform data */ +	if (pdata == NULL) { +		pdata = devm_kzalloc(&client->dev, +				     sizeof(struct lm3646_platform_data), +				     GFP_KERNEL); +		if (pdata == NULL) +			return -ENOMEM; +		/* use default data in case of no platform data */ +		pdata->flash_timeout = LM3646_FLASH_TOUT_MAX; +		pdata->led1_torch_brt = LM3646_LED1_TORCH_BRT_MAX; +		pdata->led1_flash_brt = LM3646_LED1_FLASH_BRT_MAX; +	} +	flash->pdata = pdata; +	flash->dev = &client->dev; + +	rval = lm3646_subdev_init(flash); +	if (rval < 0) +		return rval; + +	rval = lm3646_init_device(flash); +	if (rval < 0) +		return rval; + +	i2c_set_clientdata(client, flash); + +	return 0; +} + +static int lm3646_remove(struct i2c_client *client) +{ +	struct lm3646_flash *flash = i2c_get_clientdata(client); + +	v4l2_device_unregister_subdev(&flash->subdev_led); +	v4l2_ctrl_handler_free(&flash->ctrls_led); +	media_entity_cleanup(&flash->subdev_led.entity); + +	return 0; +} + +static const struct i2c_device_id lm3646_id_table[] = { +	{LM3646_NAME, 0}, +	{} +}; + +MODULE_DEVICE_TABLE(i2c, lm3646_id_table); + +static struct i2c_driver lm3646_i2c_driver = { +	.driver = { +		   .name = LM3646_NAME, +		   }, +	.probe = lm3646_probe, +	.remove = lm3646_remove, +	.id_table = lm3646_id_table, +}; + +module_i2c_driver(lm3646_i2c_driver); + +MODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>"); +MODULE_AUTHOR("Ldd Mlp <ldd-mlp@list.ti.com>"); +MODULE_DESCRIPTION("Texas Instruments LM3646 Dual Flash LED driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/m5mols/m5mols_capture.c b/drivers/media/i2c/m5mols/m5mols_capture.c index ab34ccedf31..1a03d02bd4d 100644 --- a/drivers/media/i2c/m5mols/m5mols_capture.c +++ b/drivers/media/i2c/m5mols/m5mols_capture.c @@ -26,7 +26,7 @@  #include <media/v4l2-device.h>  #include <media/v4l2-subdev.h>  #include <media/m5mols.h> -#include <media/s5p_fimc.h> +#include <media/exynos-fimc.h>  #include "m5mols.h"  #include "m5mols_reg.h" diff --git a/drivers/media/i2c/m5mols/m5mols_controls.c b/drivers/media/i2c/m5mols/m5mols_controls.c index f34429e452a..a60931e6631 100644 --- a/drivers/media/i2c/m5mols/m5mols_controls.c +++ b/drivers/media/i2c/m5mols/m5mols_controls.c @@ -544,7 +544,7 @@ int m5mols_init_controls(struct v4l2_subdev *sd)  	u16 zoom_step;  	int ret; -	/* Determine the firmware dependant control range and step values */ +	/* Determine the firmware dependent control range and step values */  	ret = m5mols_read_u16(sd, AE_MAX_GAIN_MON, &exposure_max);  	if (ret < 0)  		return ret; diff --git a/drivers/media/i2c/ml86v7667.c b/drivers/media/i2c/ml86v7667.c index a9110d8bbbc..2cace7313a2 100644 --- a/drivers/media/i2c/ml86v7667.c +++ b/drivers/media/i2c/ml86v7667.c @@ -276,6 +276,7 @@ static const struct v4l2_ctrl_ops ml86v7667_ctrl_ops = {  };  static struct v4l2_subdev_video_ops ml86v7667_subdev_video_ops = { +	.s_std = ml86v7667_s_std,  	.querystd = ml86v7667_querystd,  	.g_input_status = ml86v7667_g_input_status,  	.enum_mbus_fmt = ml86v7667_enum_mbus_fmt, @@ -286,7 +287,6 @@ static struct v4l2_subdev_video_ops ml86v7667_subdev_video_ops = {  };  static struct v4l2_subdev_core_ops ml86v7667_subdev_core_ops = { -	.s_std = ml86v7667_s_std,  #ifdef CONFIG_VIDEO_ADV_DEBUG  	.g_register = ml86v7667_g_register,  	.s_register = ml86v7667_s_register, diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c index 8190fec6808..4d9c6bc3426 100644 --- a/drivers/media/i2c/msp3400-driver.c +++ b/drivers/media/i2c/msp3400-driver.c @@ -649,10 +649,10 @@ static const struct v4l2_subdev_core_ops msp_core_ops = {  	.s_ctrl = v4l2_subdev_s_ctrl,  	.queryctrl = v4l2_subdev_queryctrl,  	.querymenu = v4l2_subdev_querymenu, -	.s_std = msp_s_std,  };  static const struct v4l2_subdev_video_ops msp_video_ops = { +	.s_std = msp_s_std,  	.querystd = msp_querystd,  }; diff --git a/drivers/media/i2c/mt9m032.c b/drivers/media/i2c/mt9m032.c index 846b15f0bf6..85ec3bacdf1 100644 --- a/drivers/media/i2c/mt9m032.c +++ b/drivers/media/i2c/mt9m032.c @@ -459,13 +459,15 @@ static int mt9m032_set_pad_crop(struct v4l2_subdev *subdev,  			  MT9M032_COLUMN_START_MAX);  	rect.top = clamp(ALIGN(crop->rect.top, 2), MT9M032_ROW_START_MIN,  			 MT9M032_ROW_START_MAX); -	rect.width = clamp(ALIGN(crop->rect.width, 2), MT9M032_COLUMN_SIZE_MIN, -			   MT9M032_COLUMN_SIZE_MAX); -	rect.height = clamp(ALIGN(crop->rect.height, 2), MT9M032_ROW_SIZE_MIN, -			    MT9M032_ROW_SIZE_MAX); - -	rect.width = min(rect.width, MT9M032_PIXEL_ARRAY_WIDTH - rect.left); -	rect.height = min(rect.height, MT9M032_PIXEL_ARRAY_HEIGHT - rect.top); +	rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2), +			     MT9M032_COLUMN_SIZE_MIN, MT9M032_COLUMN_SIZE_MAX); +	rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2), +			      MT9M032_ROW_SIZE_MIN, MT9M032_ROW_SIZE_MAX); + +	rect.width = min_t(unsigned int, rect.width, +			   MT9M032_PIXEL_ARRAY_WIDTH - rect.left); +	rect.height = min_t(unsigned int, rect.height, +			    MT9M032_PIXEL_ARRAY_HEIGHT - rect.top);  	__crop = __mt9m032_get_pad_crop(sensor, fh, crop->which); diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index 4734836fe5a..e18797ff7fa 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -19,7 +19,9 @@  #include <linux/i2c.h>  #include <linux/log2.h>  #include <linux/module.h> +#include <linux/of.h>  #include <linux/of_gpio.h> +#include <linux/of_graph.h>  #include <linux/pm.h>  #include <linux/regulator/consumer.h>  #include <linux/slab.h> @@ -28,7 +30,6 @@  #include <media/mt9p031.h>  #include <media/v4l2-ctrls.h>  #include <media/v4l2-device.h> -#include <media/v4l2-of.h>  #include <media/v4l2-subdev.h>  #include "aptina-pll.h" @@ -77,6 +78,9 @@  #define	MT9P031_PLL_CONFIG_1				0x11  #define	MT9P031_PLL_CONFIG_2				0x12  #define MT9P031_PIXEL_CLOCK_CONTROL			0x0a +#define		MT9P031_PIXEL_CLOCK_INVERT		(1 << 15) +#define		MT9P031_PIXEL_CLOCK_SHIFT(n)		((n) << 8) +#define		MT9P031_PIXEL_CLOCK_DIVIDE(n)		((n) << 0)  #define MT9P031_FRAME_RESTART				0x0b  #define MT9P031_SHUTTER_DELAY				0x0c  #define MT9P031_RST					0x0d @@ -129,6 +133,8 @@ struct mt9p031 {  	enum mt9p031_model model;  	struct aptina_pll pll; +	unsigned int clk_div; +	bool use_pll;  	int reset;  	struct v4l2_ctrl_handler ctrls; @@ -197,6 +203,11 @@ static int mt9p031_reset(struct mt9p031 *mt9p031)  	if (ret < 0)  		return ret; +	ret = mt9p031_write(client, MT9P031_PIXEL_CLOCK_CONTROL, +			    MT9P031_PIXEL_CLOCK_DIVIDE(mt9p031->clk_div)); +	if (ret < 0) +		return ret; +  	return mt9p031_set_output_control(mt9p031, MT9P031_OUTPUT_CONTROL_CEN,  					  0);  } @@ -221,15 +232,34 @@ static int mt9p031_clk_setup(struct mt9p031 *mt9p031)  	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);  	struct mt9p031_platform_data *pdata = mt9p031->pdata; +	int ret;  	mt9p031->clk = devm_clk_get(&client->dev, NULL);  	if (IS_ERR(mt9p031->clk))  		return PTR_ERR(mt9p031->clk); -	clk_set_rate(mt9p031->clk, pdata->ext_freq); +	ret = clk_set_rate(mt9p031->clk, pdata->ext_freq); +	if (ret < 0) +		return ret; + +	/* If the external clock frequency is out of bounds for the PLL use the +	 * pixel clock divider only and disable the PLL. +	 */ +	if (pdata->ext_freq > limits.ext_clock_max) { +		unsigned int div; + +		div = DIV_ROUND_UP(pdata->ext_freq, pdata->target_freq); +		div = roundup_pow_of_two(div) / 2; + +		mt9p031->clk_div = max_t(unsigned int, div, 64); +		mt9p031->use_pll = false; + +		return 0; +	}  	mt9p031->pll.ext_clock = pdata->ext_freq;  	mt9p031->pll.pix_clock = pdata->target_freq; +	mt9p031->use_pll = true;  	return aptina_pll_calculate(&client->dev, &limits, &mt9p031->pll);  } @@ -239,6 +269,9 @@ static int mt9p031_pll_enable(struct mt9p031 *mt9p031)  	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);  	int ret; +	if (!mt9p031->use_pll) +		return 0; +  	ret = mt9p031_write(client, MT9P031_PLL_CONTROL,  			    MT9P031_PLL_CONTROL_PWRON);  	if (ret < 0) @@ -264,6 +297,9 @@ static inline int mt9p031_pll_disable(struct mt9p031 *mt9p031)  {  	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); +	if (!mt9p031->use_pll) +		return 0; +  	return mt9p031_write(client, MT9P031_PLL_CONTROL,  			     MT9P031_PLL_CONTROL_PWROFF);  } @@ -284,9 +320,15 @@ static int mt9p031_power_on(struct mt9p031 *mt9p031)  	if (ret < 0)  		return ret; -	/* Emable clock */ -	if (mt9p031->clk) -		clk_prepare_enable(mt9p031->clk); +	/* Enable clock */ +	if (mt9p031->clk) { +		ret = clk_prepare_enable(mt9p031->clk); +		if (ret) { +			regulator_bulk_disable(ARRAY_SIZE(mt9p031->regulators), +					       mt9p031->regulators); +			return ret; +		} +	}  	/* Now RESET_BAR must be high */  	if (gpio_is_valid(mt9p031->reset)) { @@ -518,11 +560,13 @@ static int mt9p031_set_format(struct v4l2_subdev *subdev,  	/* Clamp the width and height to avoid dividing by zero. */  	width = clamp_t(unsigned int, ALIGN(format->format.width, 2), -			max(__crop->width / 7, MT9P031_WINDOW_WIDTH_MIN), +			max_t(unsigned int, __crop->width / 7, +			      MT9P031_WINDOW_WIDTH_MIN),  			__crop->width);  	height = clamp_t(unsigned int, ALIGN(format->format.height, 2), -			max(__crop->height / 8, MT9P031_WINDOW_HEIGHT_MIN), -			__crop->height); +			 max_t(unsigned int, __crop->height / 8, +			       MT9P031_WINDOW_HEIGHT_MIN), +			 __crop->height);  	hratio = DIV_ROUND_CLOSEST(__crop->width, width);  	vratio = DIV_ROUND_CLOSEST(__crop->height, height); @@ -564,15 +608,17 @@ static int mt9p031_set_crop(struct v4l2_subdev *subdev,  			  MT9P031_COLUMN_START_MAX);  	rect.top = clamp(ALIGN(crop->rect.top, 2), MT9P031_ROW_START_MIN,  			 MT9P031_ROW_START_MAX); -	rect.width = clamp(ALIGN(crop->rect.width, 2), -			   MT9P031_WINDOW_WIDTH_MIN, -			   MT9P031_WINDOW_WIDTH_MAX); -	rect.height = clamp(ALIGN(crop->rect.height, 2), -			    MT9P031_WINDOW_HEIGHT_MIN, -			    MT9P031_WINDOW_HEIGHT_MAX); - -	rect.width = min(rect.width, MT9P031_PIXEL_ARRAY_WIDTH - rect.left); -	rect.height = min(rect.height, MT9P031_PIXEL_ARRAY_HEIGHT - rect.top); +	rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2), +			     MT9P031_WINDOW_WIDTH_MIN, +			     MT9P031_WINDOW_WIDTH_MAX); +	rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2), +			      MT9P031_WINDOW_HEIGHT_MIN, +			      MT9P031_WINDOW_HEIGHT_MAX); + +	rect.width = min_t(unsigned int, rect.width, +			   MT9P031_PIXEL_ARRAY_WIDTH - rect.left); +	rect.height = min_t(unsigned int, rect.height, +			    MT9P031_PIXEL_ARRAY_HEIGHT - rect.top);  	__crop = __mt9p031_get_pad_crop(mt9p031, fh, crop->pad, crop->which); @@ -601,6 +647,28 @@ static int mt9p031_set_crop(struct v4l2_subdev *subdev,  #define V4L2_CID_BLC_ANALOG_OFFSET	(V4L2_CID_USER_BASE | 0x1004)  #define V4L2_CID_BLC_DIGITAL_OFFSET	(V4L2_CID_USER_BASE | 0x1005) +static int mt9p031_restore_blc(struct mt9p031 *mt9p031) +{ +	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); +	int ret; + +	if (mt9p031->blc_auto->cur.val != 0) { +		ret = mt9p031_set_mode2(mt9p031, 0, +					MT9P031_READ_MODE_2_ROW_BLC); +		if (ret < 0) +			return ret; +	} + +	if (mt9p031->blc_offset->cur.val != 0) { +		ret = mt9p031_write(client, MT9P031_ROW_BLACK_TARGET, +				    mt9p031->blc_offset->cur.val); +		if (ret < 0) +			return ret; +	} + +	return 0; +} +  static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)  {  	struct mt9p031 *mt9p031 = @@ -609,6 +677,9 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)  	u16 data;  	int ret; +	if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) +		return 0; +  	switch (ctrl->id) {  	case V4L2_CID_EXPOSURE:  		ret = mt9p031_write(client, MT9P031_SHUTTER_WIDTH_UPPER, @@ -663,18 +734,20 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)  					MT9P031_READ_MODE_2_ROW_MIR, 0);  	case V4L2_CID_TEST_PATTERN: +		/* The digital side of the Black Level Calibration function must +		 * be disabled when generating a test pattern to avoid artifacts +		 * in the image. Activate (deactivate) the BLC-related controls +		 * when the test pattern is enabled (disabled). +		 */ +		v4l2_ctrl_activate(mt9p031->blc_auto, ctrl->val == 0); +		v4l2_ctrl_activate(mt9p031->blc_offset, ctrl->val == 0); +  		if (!ctrl->val) { -			/* Restore the black level compensation settings. */ -			if (mt9p031->blc_auto->cur.val != 0) { -				ret = mt9p031_s_ctrl(mt9p031->blc_auto); -				if (ret < 0) -					return ret; -			} -			if (mt9p031->blc_offset->cur.val != 0) { -				ret = mt9p031_s_ctrl(mt9p031->blc_offset); -				if (ret < 0) -					return ret; -			} +			/* Restore the BLC settings. */ +			ret = mt9p031_restore_blc(mt9p031); +			if (ret < 0) +				return ret; +  			return mt9p031_write(client, MT9P031_TEST_PATTERN,  					     MT9P031_TEST_PATTERN_DISABLE);  		} @@ -689,9 +762,7 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)  		if (ret < 0)  			return ret; -		/* Disable digital black level compensation when using a test -		 * pattern. -		 */ +		/* Disable digital BLC when generating a test pattern. */  		ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC,  					0);  		if (ret < 0) @@ -938,7 +1009,7 @@ mt9p031_get_pdata(struct i2c_client *client)  	if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node)  		return client->dev.platform_data; -	np = v4l2_of_get_next_endpoint(client->dev.of_node, NULL); +	np = of_graph_get_next_endpoint(client->dev.of_node, NULL);  	if (!np)  		return NULL; diff --git a/drivers/media/i2c/mt9t001.c b/drivers/media/i2c/mt9t001.c index 796463466ef..422e068f5f1 100644 --- a/drivers/media/i2c/mt9t001.c +++ b/drivers/media/i2c/mt9t001.c @@ -12,9 +12,11 @@   * published by the Free Software Foundation.   */ +#include <linux/clk.h>  #include <linux/i2c.h> -#include <linux/module.h>  #include <linux/log2.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h>  #include <linux/slab.h>  #include <linux/videodev2.h>  #include <linux/v4l2-mediabus.h> @@ -55,6 +57,7 @@  #define		MT9T001_OUTPUT_CONTROL_SYNC		(1 << 0)  #define		MT9T001_OUTPUT_CONTROL_CHIP_ENABLE	(1 << 1)  #define		MT9T001_OUTPUT_CONTROL_TEST_DATA	(1 << 6) +#define		MT9T001_OUTPUT_CONTROL_DEF		0x0002  #define MT9T001_SHUTTER_WIDTH_HIGH			0x08  #define MT9T001_SHUTTER_WIDTH_LOW			0x09  #define		MT9T001_SHUTTER_WIDTH_MIN		1 @@ -116,6 +119,12 @@ struct mt9t001 {  	struct v4l2_subdev subdev;  	struct media_pad pad; +	struct clk *clk; +	struct regulator_bulk_data regulators[2]; + +	struct mutex power_lock; /* lock to protect power_count */ +	int power_count; +  	struct v4l2_mbus_framefmt format;  	struct v4l2_rect crop; @@ -159,6 +168,77 @@ static int mt9t001_set_output_control(struct mt9t001 *mt9t001, u16 clear,  	return 0;  } +static int mt9t001_reset(struct mt9t001 *mt9t001) +{ +	struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev); +	int ret; + +	/* Reset the chip and stop data read out */ +	ret = mt9t001_write(client, MT9T001_RESET, 1); +	if (ret < 0) +		return ret; + +	ret = mt9t001_write(client, MT9T001_RESET, 0); +	if (ret < 0) +		return ret; + +	mt9t001->output_control = MT9T001_OUTPUT_CONTROL_DEF; + +	return mt9t001_set_output_control(mt9t001, +					  MT9T001_OUTPUT_CONTROL_CHIP_ENABLE, +					  0); +} + +static int mt9t001_power_on(struct mt9t001 *mt9t001) +{ +	int ret; + +	/* Bring up the supplies */ +	ret = regulator_bulk_enable(ARRAY_SIZE(mt9t001->regulators), +				   mt9t001->regulators); +	if (ret < 0) +		return ret; + +	/* Enable clock */ +	ret = clk_prepare_enable(mt9t001->clk); +	if (ret < 0) +		regulator_bulk_disable(ARRAY_SIZE(mt9t001->regulators), +				       mt9t001->regulators); + +	return ret; +} + +static void mt9t001_power_off(struct mt9t001 *mt9t001) +{ +	regulator_bulk_disable(ARRAY_SIZE(mt9t001->regulators), +			       mt9t001->regulators); + +	clk_disable_unprepare(mt9t001->clk); +} + +static int __mt9t001_set_power(struct mt9t001 *mt9t001, bool on) +{ +	struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev); +	int ret; + +	if (!on) { +		mt9t001_power_off(mt9t001); +		return 0; +	} + +	ret = mt9t001_power_on(mt9t001); +	if (ret < 0) +		return ret; + +	ret = mt9t001_reset(mt9t001); +	if (ret < 0) { +		dev_err(&client->dev, "Failed to reset the camera\n"); +		return ret; +	} + +	return v4l2_ctrl_handler_setup(&mt9t001->ctrls); +} +  /* -----------------------------------------------------------------------------   * V4L2 subdev video operations   */ @@ -195,6 +275,7 @@ static int mt9t001_s_stream(struct v4l2_subdev *subdev, int enable)  {  	const u16 mode = MT9T001_OUTPUT_CONTROL_CHIP_ENABLE;  	struct i2c_client *client = v4l2_get_subdevdata(subdev); +	struct mt9t001_platform_data *pdata = client->dev.platform_data;  	struct mt9t001 *mt9t001 = to_mt9t001(subdev);  	struct v4l2_mbus_framefmt *format = &mt9t001->format;  	struct v4l2_rect *crop = &mt9t001->crop; @@ -205,6 +286,14 @@ static int mt9t001_s_stream(struct v4l2_subdev *subdev, int enable)  	if (!enable)  		return mt9t001_set_output_control(mt9t001, mode, 0); +	/* Configure the pixel clock polarity */ +	if (pdata->clk_pol) { +		ret  = mt9t001_write(client, MT9T001_PIXEL_CLOCK, +				     MT9T001_PIXEL_CLOCK_INVERT); +		if (ret < 0) +			return ret; +	} +  	/* Configure the window size and row/column bin */  	hratio = DIV_ROUND_CLOSEST(crop->width, format->width);  	vratio = DIV_ROUND_CLOSEST(crop->height, format->height); @@ -291,10 +380,12 @@ static int mt9t001_set_format(struct v4l2_subdev *subdev,  	/* Clamp the width and height to avoid dividing by zero. */  	width = clamp_t(unsigned int, ALIGN(format->format.width, 2), -			max(__crop->width / 8, MT9T001_WINDOW_HEIGHT_MIN + 1), +			max_t(unsigned int, __crop->width / 8, +			      MT9T001_WINDOW_HEIGHT_MIN + 1),  			__crop->width);  	height = clamp_t(unsigned int, ALIGN(format->format.height, 2), -			 max(__crop->height / 8, MT9T001_WINDOW_HEIGHT_MIN + 1), +			 max_t(unsigned int, __crop->height / 8, +			       MT9T001_WINDOW_HEIGHT_MIN + 1),  			 __crop->height);  	hratio = DIV_ROUND_CLOSEST(__crop->width, width); @@ -339,15 +430,17 @@ static int mt9t001_set_crop(struct v4l2_subdev *subdev,  	rect.top = clamp(ALIGN(crop->rect.top, 2),  			 MT9T001_ROW_START_MIN,  			 MT9T001_ROW_START_MAX); -	rect.width = clamp(ALIGN(crop->rect.width, 2), -			   MT9T001_WINDOW_WIDTH_MIN + 1, -			   MT9T001_WINDOW_WIDTH_MAX + 1); -	rect.height = clamp(ALIGN(crop->rect.height, 2), -			    MT9T001_WINDOW_HEIGHT_MIN + 1, -			    MT9T001_WINDOW_HEIGHT_MAX + 1); - -	rect.width = min(rect.width, MT9T001_PIXEL_ARRAY_WIDTH - rect.left); -	rect.height = min(rect.height, MT9T001_PIXEL_ARRAY_HEIGHT - rect.top); +	rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2), +			     MT9T001_WINDOW_WIDTH_MIN + 1, +			     MT9T001_WINDOW_WIDTH_MAX + 1); +	rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2), +			      MT9T001_WINDOW_HEIGHT_MIN + 1, +			      MT9T001_WINDOW_HEIGHT_MAX + 1); + +	rect.width = min_t(unsigned int, rect.width, +			   MT9T001_PIXEL_ARRAY_WIDTH - rect.left); +	rect.height = min_t(unsigned int, rect.height, +			    MT9T001_PIXEL_ARRAY_HEIGHT - rect.top);  	__crop = __mt9t001_get_pad_crop(mt9t001, fh, crop->pad, crop->which); @@ -626,9 +719,67 @@ static const struct v4l2_ctrl_config mt9t001_gains[] = {  };  /* ----------------------------------------------------------------------------- + * V4L2 subdev core operations + */ + +static int mt9t001_set_power(struct v4l2_subdev *subdev, int on) +{ +	struct mt9t001 *mt9t001 = to_mt9t001(subdev); +	int ret = 0; + +	mutex_lock(&mt9t001->power_lock); + +	/* If the power count is modified from 0 to != 0 or from != 0 to 0, +	 * update the power state. +	 */ +	if (mt9t001->power_count == !on) { +		ret = __mt9t001_set_power(mt9t001, !!on); +		if (ret < 0) +			goto out; +	} + +	/* Update the power count. */ +	mt9t001->power_count += on ? 1 : -1; +	WARN_ON(mt9t001->power_count < 0); + +out: +	mutex_unlock(&mt9t001->power_lock); +	return ret; +} + +/* -----------------------------------------------------------------------------   * V4L2 subdev internal operations   */ +static int mt9t001_registered(struct v4l2_subdev *subdev) +{ +	struct i2c_client *client = v4l2_get_subdevdata(subdev); +	struct mt9t001 *mt9t001 = to_mt9t001(subdev); +	s32 data; +	int ret; + +	ret = mt9t001_power_on(mt9t001); +	if (ret < 0) { +		dev_err(&client->dev, "MT9T001 power up failed\n"); +		return ret; +	} + +	/* Read out the chip version register */ +	data = mt9t001_read(client, MT9T001_CHIP_VERSION); +	mt9t001_power_off(mt9t001); + +	if (data != MT9T001_CHIP_ID) { +		dev_err(&client->dev, +			"MT9T001 not detected, wrong version 0x%04x\n", data); +		return -ENODEV; +	} + +	dev_info(&client->dev, "MT9T001 detected at address 0x%02x\n", +		 client->addr); + +	return 0; +} +  static int mt9t001_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)  {  	struct v4l2_mbus_framefmt *format; @@ -647,9 +798,18 @@ static int mt9t001_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)  	format->field = V4L2_FIELD_NONE;  	format->colorspace = V4L2_COLORSPACE_SRGB; -	return 0; +	return mt9t001_set_power(subdev, 1); +} + +static int mt9t001_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ +	return mt9t001_set_power(subdev, 0);  } +static struct v4l2_subdev_core_ops mt9t001_subdev_core_ops = { +	.s_power = mt9t001_set_power, +}; +  static struct v4l2_subdev_video_ops mt9t001_subdev_video_ops = {  	.s_stream = mt9t001_s_stream,  }; @@ -664,58 +824,17 @@ static struct v4l2_subdev_pad_ops mt9t001_subdev_pad_ops = {  };  static struct v4l2_subdev_ops mt9t001_subdev_ops = { +	.core = &mt9t001_subdev_core_ops,  	.video = &mt9t001_subdev_video_ops,  	.pad = &mt9t001_subdev_pad_ops,  };  static struct v4l2_subdev_internal_ops mt9t001_subdev_internal_ops = { +	.registered = mt9t001_registered,  	.open = mt9t001_open, +	.close = mt9t001_close,  }; -static int mt9t001_video_probe(struct i2c_client *client) -{ -	struct mt9t001_platform_data *pdata = client->dev.platform_data; -	s32 data; -	int ret; - -	dev_info(&client->dev, "Probing MT9T001 at address 0x%02x\n", -		 client->addr); - -	/* Reset the chip and stop data read out */ -	ret = mt9t001_write(client, MT9T001_RESET, 1); -	if (ret < 0) -		return ret; - -	ret = mt9t001_write(client, MT9T001_RESET, 0); -	if (ret < 0) -		return ret; - -	ret  = mt9t001_write(client, MT9T001_OUTPUT_CONTROL, 0); -	if (ret < 0) -		return ret; - -	/* Configure the pixel clock polarity */ -	if (pdata->clk_pol) { -		ret  = mt9t001_write(client, MT9T001_PIXEL_CLOCK, -				     MT9T001_PIXEL_CLOCK_INVERT); -		if (ret < 0) -			return ret; -	} - -	/* Read and check the sensor version */ -	data = mt9t001_read(client, MT9T001_CHIP_VERSION); -	if (data != MT9T001_CHIP_ID) { -		dev_err(&client->dev, "MT9T001 not detected, wrong version " -			"0x%04x\n", data); -		return -ENODEV; -	} - -	dev_info(&client->dev, "MT9T001 detected at address 0x%02x\n", -		 client->addr); - -	return ret; -} -  static int mt9t001_probe(struct i2c_client *client,  			 const struct i2c_device_id *did)  { @@ -736,14 +855,28 @@ static int mt9t001_probe(struct i2c_client *client,  		return -EIO;  	} -	ret = mt9t001_video_probe(client); -	if (ret < 0) -		return ret; -  	mt9t001 = devm_kzalloc(&client->dev, sizeof(*mt9t001), GFP_KERNEL);  	if (!mt9t001)  		return -ENOMEM; +	mutex_init(&mt9t001->power_lock); +	mt9t001->output_control = MT9T001_OUTPUT_CONTROL_DEF; + +	mt9t001->regulators[0].supply = "vdd"; +	mt9t001->regulators[1].supply = "vaa"; + +	ret = devm_regulator_bulk_get(&client->dev, 2, mt9t001->regulators); +	if (ret < 0) { +		dev_err(&client->dev, "Unable to get regulators\n"); +		return ret; +	} + +	mt9t001->clk = devm_clk_get(&client->dev, NULL); +	if (IS_ERR(mt9t001->clk)) { +		dev_err(&client->dev, "Unable to get clock\n"); +		return PTR_ERR(mt9t001->clk); +	} +  	v4l2_ctrl_handler_init(&mt9t001->ctrls, ARRAY_SIZE(mt9t001_ctrls) +  						ARRAY_SIZE(mt9t001_gains) + 4); diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c index f74698cf14c..47e475319a2 100644 --- a/drivers/media/i2c/mt9v011.c +++ b/drivers/media/i2c/mt9v011.c @@ -1,7 +1,7 @@  /*   * mt9v011 -Micron 1/4-Inch VGA Digital Image Sensor   * - * Copyright (c) 2009 Mauro Carvalho Chehab (mchehab@redhat.com) + * Copyright (c) 2009 Mauro Carvalho Chehab   * This code is placed under the terms of the GNU General Public License v2   */ @@ -16,7 +16,7 @@  #include <media/mt9v011.h>  MODULE_DESCRIPTION("Micron mt9v011 sensor driver"); -MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); +MODULE_AUTHOR("Mauro Carvalho Chehab");  MODULE_LICENSE("GPL");  static int debug; diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 2c50effaa33..40172b8d8ea 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -27,14 +27,16 @@  #include <media/v4l2-device.h>  #include <media/v4l2-subdev.h> -#define MT9V032_PIXEL_ARRAY_HEIGHT			492 -#define MT9V032_PIXEL_ARRAY_WIDTH			782 +/* The first four rows are black rows. The active area spans 753x481 pixels. */ +#define MT9V032_PIXEL_ARRAY_HEIGHT			485 +#define MT9V032_PIXEL_ARRAY_WIDTH			753  #define MT9V032_SYSCLK_FREQ_DEF				26600000  #define MT9V032_CHIP_VERSION				0x00  #define		MT9V032_CHIP_ID_REV1			0x1311  #define		MT9V032_CHIP_ID_REV3			0x1313 +#define		MT9V034_CHIP_ID_REV1			0X1324  #define MT9V032_COLUMN_START				0x01  #define		MT9V032_COLUMN_START_MIN		1  #define		MT9V032_COLUMN_START_DEF		1 @@ -53,12 +55,15 @@  #define		MT9V032_WINDOW_WIDTH_MAX		752  #define MT9V032_HORIZONTAL_BLANKING			0x05  #define		MT9V032_HORIZONTAL_BLANKING_MIN		43 +#define		MT9V034_HORIZONTAL_BLANKING_MIN		61  #define		MT9V032_HORIZONTAL_BLANKING_DEF		94  #define		MT9V032_HORIZONTAL_BLANKING_MAX		1023  #define MT9V032_VERTICAL_BLANKING			0x06  #define		MT9V032_VERTICAL_BLANKING_MIN		4 +#define		MT9V034_VERTICAL_BLANKING_MIN		2  #define		MT9V032_VERTICAL_BLANKING_DEF		45  #define		MT9V032_VERTICAL_BLANKING_MAX		3000 +#define		MT9V034_VERTICAL_BLANKING_MAX		32288  #define MT9V032_CHIP_CONTROL				0x07  #define		MT9V032_CHIP_CONTROL_MASTER_MODE	(1 << 3)  #define		MT9V032_CHIP_CONTROL_DOUT_ENABLE	(1 << 7) @@ -68,8 +73,10 @@  #define MT9V032_SHUTTER_WIDTH_CONTROL			0x0a  #define MT9V032_TOTAL_SHUTTER_WIDTH			0x0b  #define		MT9V032_TOTAL_SHUTTER_WIDTH_MIN		1 +#define		MT9V034_TOTAL_SHUTTER_WIDTH_MIN		0  #define		MT9V032_TOTAL_SHUTTER_WIDTH_DEF		480  #define		MT9V032_TOTAL_SHUTTER_WIDTH_MAX		32767 +#define		MT9V034_TOTAL_SHUTTER_WIDTH_MAX		32765  #define MT9V032_RESET					0x0c  #define MT9V032_READ_MODE				0x0d  #define		MT9V032_READ_MODE_ROW_BIN_MASK		(3 << 0) @@ -81,6 +88,8 @@  #define		MT9V032_READ_MODE_DARK_COLUMNS		(1 << 6)  #define		MT9V032_READ_MODE_DARK_ROWS		(1 << 7)  #define MT9V032_PIXEL_OPERATION_MODE			0x0f +#define		MT9V034_PIXEL_OPERATION_MODE_HDR	(1 << 0) +#define		MT9V034_PIXEL_OPERATION_MODE_COLOR	(1 << 1)  #define		MT9V032_PIXEL_OPERATION_MODE_COLOR	(1 << 2)  #define		MT9V032_PIXEL_OPERATION_MODE_HDR	(1 << 6)  #define MT9V032_ANALOG_GAIN				0x35 @@ -96,9 +105,12 @@  #define		MT9V032_DARK_AVG_HIGH_THRESH_MASK	(255 << 8)  #define		MT9V032_DARK_AVG_HIGH_THRESH_SHIFT	8  #define MT9V032_ROW_NOISE_CORR_CONTROL			0x70 +#define		MT9V034_ROW_NOISE_CORR_ENABLE		(1 << 0) +#define		MT9V034_ROW_NOISE_CORR_USE_BLK_AVG	(1 << 1)  #define		MT9V032_ROW_NOISE_CORR_ENABLE		(1 << 5)  #define		MT9V032_ROW_NOISE_CORR_USE_BLK_AVG	(1 << 7)  #define MT9V032_PIXEL_CLOCK				0x74 +#define MT9V034_PIXEL_CLOCK				0x72  #define		MT9V032_PIXEL_CLOCK_INV_LINE		(1 << 0)  #define		MT9V032_PIXEL_CLOCK_INV_FRAME		(1 << 1)  #define		MT9V032_PIXEL_CLOCK_XOR_LINE		(1 << 2) @@ -120,12 +132,88 @@  #define		MT9V032_AGC_ENABLE			(1 << 1)  #define MT9V032_THERMAL_INFO				0xc1 +enum mt9v032_model { +	MT9V032_MODEL_V032_COLOR, +	MT9V032_MODEL_V032_MONO, +	MT9V032_MODEL_V034_COLOR, +	MT9V032_MODEL_V034_MONO, +}; + +struct mt9v032_model_version { +	unsigned int version; +	const char *name; +}; + +struct mt9v032_model_data { +	unsigned int min_row_time; +	unsigned int min_hblank; +	unsigned int min_vblank; +	unsigned int max_vblank; +	unsigned int min_shutter; +	unsigned int max_shutter; +	unsigned int pclk_reg; +}; + +struct mt9v032_model_info { +	const struct mt9v032_model_data *data; +	bool color; +}; + +static const struct mt9v032_model_version mt9v032_versions[] = { +	{ MT9V032_CHIP_ID_REV1, "MT9V032 rev1/2" }, +	{ MT9V032_CHIP_ID_REV3, "MT9V032 rev3" }, +	{ MT9V034_CHIP_ID_REV1, "MT9V034 rev1" }, +}; + +static const struct mt9v032_model_data mt9v032_model_data[] = { +	{ +		/* MT9V032 revisions 1/2/3 */ +		.min_row_time = 660, +		.min_hblank = MT9V032_HORIZONTAL_BLANKING_MIN, +		.min_vblank = MT9V032_VERTICAL_BLANKING_MIN, +		.max_vblank = MT9V032_VERTICAL_BLANKING_MAX, +		.min_shutter = MT9V032_TOTAL_SHUTTER_WIDTH_MIN, +		.max_shutter = MT9V032_TOTAL_SHUTTER_WIDTH_MAX, +		.pclk_reg = MT9V032_PIXEL_CLOCK, +	}, { +		/* MT9V034 */ +		.min_row_time = 690, +		.min_hblank = MT9V034_HORIZONTAL_BLANKING_MIN, +		.min_vblank = MT9V034_VERTICAL_BLANKING_MIN, +		.max_vblank = MT9V034_VERTICAL_BLANKING_MAX, +		.min_shutter = MT9V034_TOTAL_SHUTTER_WIDTH_MIN, +		.max_shutter = MT9V034_TOTAL_SHUTTER_WIDTH_MAX, +		.pclk_reg = MT9V034_PIXEL_CLOCK, +	}, +}; + +static const struct mt9v032_model_info mt9v032_models[] = { +	[MT9V032_MODEL_V032_COLOR] = { +		.data = &mt9v032_model_data[0], +		.color = true, +	}, +	[MT9V032_MODEL_V032_MONO] = { +		.data = &mt9v032_model_data[0], +		.color = false, +	}, +	[MT9V032_MODEL_V034_COLOR] = { +		.data = &mt9v032_model_data[1], +		.color = true, +	}, +	[MT9V032_MODEL_V034_MONO] = { +		.data = &mt9v032_model_data[1], +		.color = false, +	}, +}; +  struct mt9v032 {  	struct v4l2_subdev subdev;  	struct media_pad pad;  	struct v4l2_mbus_framefmt format;  	struct v4l2_rect crop; +	unsigned int hratio; +	unsigned int vratio;  	struct v4l2_ctrl_handler ctrls;  	struct { @@ -139,6 +227,8 @@ struct mt9v032 {  	struct clk *clk;  	struct mt9v032_platform_data *pdata; +	const struct mt9v032_model_info *model; +	const struct mt9v032_model_version *version;  	u32 sysclk;  	u16 chip_control; @@ -210,20 +300,31 @@ mt9v032_update_hblank(struct mt9v032 *mt9v032)  {  	struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev);  	struct v4l2_rect *crop = &mt9v032->crop; +	unsigned int min_hblank = mt9v032->model->data->min_hblank; +	unsigned int hblank; -	return mt9v032_write(client, MT9V032_HORIZONTAL_BLANKING, -			     max_t(s32, mt9v032->hblank, 660 - crop->width)); -} +	if (mt9v032->version->version == MT9V034_CHIP_ID_REV1) +		min_hblank += (mt9v032->hratio - 1) * 10; +	min_hblank = max_t(unsigned int, (int)mt9v032->model->data->min_row_time - crop->width, +			   (int)min_hblank); +	hblank = max_t(unsigned int, mt9v032->hblank, min_hblank); -#define EXT_CLK		25000000 +	return mt9v032_write(client, MT9V032_HORIZONTAL_BLANKING, hblank); +}  static int mt9v032_power_on(struct mt9v032 *mt9v032)  {  	struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev);  	int ret; -	clk_set_rate(mt9v032->clk, mt9v032->sysclk); -	clk_prepare_enable(mt9v032->clk); +	ret = clk_set_rate(mt9v032->clk, mt9v032->sysclk); +	if (ret < 0) +		return ret; + +	ret = clk_prepare_enable(mt9v032->clk); +	if (ret) +		return ret; +  	udelay(1);  	/* Reset the chip and stop data read out */ @@ -259,7 +360,7 @@ static int __mt9v032_set_power(struct mt9v032 *mt9v032, bool on)  	/* Configure the pixel clock polarity */  	if (mt9v032->pdata && mt9v032->pdata->clk_pol) { -		ret = mt9v032_write(client, MT9V032_PIXEL_CLOCK, +		ret = mt9v032_write(client, mt9v032->model->data->pclk_reg,  				MT9V032_PIXEL_CLOCK_INV_PXL_CLK);  		if (ret < 0)  			return ret; @@ -312,22 +413,20 @@ static int mt9v032_s_stream(struct v4l2_subdev *subdev, int enable)  		       | MT9V032_CHIP_CONTROL_SEQUENTIAL;  	struct i2c_client *client = v4l2_get_subdevdata(subdev);  	struct mt9v032 *mt9v032 = to_mt9v032(subdev); -	struct v4l2_mbus_framefmt *format = &mt9v032->format;  	struct v4l2_rect *crop = &mt9v032->crop; -	unsigned int hratio; -	unsigned int vratio; +	unsigned int hbin; +	unsigned int vbin;  	int ret;  	if (!enable)  		return mt9v032_set_chip_control(mt9v032, mode, 0);  	/* Configure the window size and row/column bin */ -	hratio = DIV_ROUND_CLOSEST(crop->width, format->width); -	vratio = DIV_ROUND_CLOSEST(crop->height, format->height); - +	hbin = fls(mt9v032->hratio) - 1; +	vbin = fls(mt9v032->vratio) - 1;  	ret = mt9v032_write(client, MT9V032_READ_MODE, -		    (hratio - 1) << MT9V032_READ_MODE_ROW_BIN_SHIFT | -		    (vratio - 1) << MT9V032_READ_MODE_COLUMN_BIN_SHIFT); +			    hbin << MT9V032_READ_MODE_COLUMN_BIN_SHIFT | +			    vbin << MT9V032_READ_MODE_ROW_BIN_SHIFT);  	if (ret < 0)  		return ret; @@ -370,12 +469,12 @@ static int mt9v032_enum_frame_size(struct v4l2_subdev *subdev,  				   struct v4l2_subdev_fh *fh,  				   struct v4l2_subdev_frame_size_enum *fse)  { -	if (fse->index >= 8 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10) +	if (fse->index >= 3 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10)  		return -EINVAL; -	fse->min_width = MT9V032_WINDOW_WIDTH_DEF / fse->index; +	fse->min_width = MT9V032_WINDOW_WIDTH_DEF / (1 << fse->index);  	fse->max_width = fse->min_width; -	fse->min_height = MT9V032_WINDOW_HEIGHT_DEF / fse->index; +	fse->min_height = MT9V032_WINDOW_HEIGHT_DEF / (1 << fse->index);  	fse->max_height = fse->min_height;  	return 0; @@ -392,18 +491,30 @@ static int mt9v032_get_format(struct v4l2_subdev *subdev,  	return 0;  } -static void mt9v032_configure_pixel_rate(struct mt9v032 *mt9v032, -					 unsigned int hratio) +static void mt9v032_configure_pixel_rate(struct mt9v032 *mt9v032)  {  	struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev);  	int ret;  	ret = v4l2_ctrl_s_ctrl_int64(mt9v032->pixel_rate, -				     mt9v032->sysclk / hratio); +				     mt9v032->sysclk / mt9v032->hratio);  	if (ret < 0)  		dev_warn(&client->dev, "failed to set pixel rate (%d)\n", ret);  } +static unsigned int mt9v032_calc_ratio(unsigned int input, unsigned int output) +{ +	/* Compute the power-of-two binning factor closest to the input size to +	 * output size ratio. Given that the output size is bounded by input/4 +	 * and input, a generic implementation would be an ineffective luxury. +	 */ +	if (output * 3 > input * 2) +		return 1; +	if (output * 3 > input) +		return 2; +	return 4; +} +  static int mt9v032_set_format(struct v4l2_subdev *subdev,  			      struct v4l2_subdev_fh *fh,  			      struct v4l2_subdev_format *format) @@ -420,22 +531,28 @@ static int mt9v032_set_format(struct v4l2_subdev *subdev,  					format->which);  	/* Clamp the width and height to avoid dividing by zero. */ -	width = clamp_t(unsigned int, ALIGN(format->format.width, 2), -			max(__crop->width / 8, MT9V032_WINDOW_WIDTH_MIN), -			__crop->width); -	height = clamp_t(unsigned int, ALIGN(format->format.height, 2), -			 max(__crop->height / 8, MT9V032_WINDOW_HEIGHT_MIN), -			 __crop->height); - -	hratio = DIV_ROUND_CLOSEST(__crop->width, width); -	vratio = DIV_ROUND_CLOSEST(__crop->height, height); +	width = clamp(ALIGN(format->format.width, 2), +		      max_t(unsigned int, __crop->width / 4, +			    MT9V032_WINDOW_WIDTH_MIN), +		      __crop->width); +	height = clamp(ALIGN(format->format.height, 2), +		       max_t(unsigned int, __crop->height / 4, +			     MT9V032_WINDOW_HEIGHT_MIN), +		       __crop->height); + +	hratio = mt9v032_calc_ratio(__crop->width, width); +	vratio = mt9v032_calc_ratio(__crop->height, height);  	__format = __mt9v032_get_pad_format(mt9v032, fh, format->pad,  					    format->which);  	__format->width = __crop->width / hratio;  	__format->height = __crop->height / vratio; -	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) -		mt9v032_configure_pixel_rate(mt9v032, hratio); + +	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { +		mt9v032->hratio = hratio; +		mt9v032->vratio = vratio; +		mt9v032_configure_pixel_rate(mt9v032); +	}  	format->format = *__format; @@ -471,15 +588,17 @@ static int mt9v032_set_crop(struct v4l2_subdev *subdev,  	rect.top = clamp(ALIGN(crop->rect.top + 1, 2) - 1,  			 MT9V032_ROW_START_MIN,  			 MT9V032_ROW_START_MAX); -	rect.width = clamp(ALIGN(crop->rect.width, 2), -			   MT9V032_WINDOW_WIDTH_MIN, -			   MT9V032_WINDOW_WIDTH_MAX); -	rect.height = clamp(ALIGN(crop->rect.height, 2), -			    MT9V032_WINDOW_HEIGHT_MIN, -			    MT9V032_WINDOW_HEIGHT_MAX); - -	rect.width = min(rect.width, MT9V032_PIXEL_ARRAY_WIDTH - rect.left); -	rect.height = min(rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top); +	rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2), +			     MT9V032_WINDOW_WIDTH_MIN, +			     MT9V032_WINDOW_WIDTH_MAX); +	rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2), +			      MT9V032_WINDOW_HEIGHT_MIN, +			      MT9V032_WINDOW_HEIGHT_MAX); + +	rect.width = min_t(unsigned int, +			   rect.width, MT9V032_PIXEL_ARRAY_WIDTH - rect.left); +	rect.height = min_t(unsigned int, +			    rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top);  	__crop = __mt9v032_get_pad_crop(mt9v032, fh, crop->pad, crop->which); @@ -491,8 +610,11 @@ static int mt9v032_set_crop(struct v4l2_subdev *subdev,  						    crop->which);  		__format->width = rect.width;  		__format->height = rect.height; -		if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) -			mt9v032_configure_pixel_rate(mt9v032, 1); +		if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { +			mt9v032->hratio = 1; +			mt9v032->vratio = 1; +			mt9v032_configure_pixel_rate(mt9v032); +		}  	}  	*__crop = rect; @@ -641,7 +763,8 @@ static int mt9v032_registered(struct v4l2_subdev *subdev)  {  	struct i2c_client *client = v4l2_get_subdevdata(subdev);  	struct mt9v032 *mt9v032 = to_mt9v032(subdev); -	s32 data; +	unsigned int i; +	s32 version;  	int ret;  	dev_info(&client->dev, "Probing MT9V032 at address 0x%02x\n", @@ -654,25 +777,38 @@ static int mt9v032_registered(struct v4l2_subdev *subdev)  	}  	/* Read and check the sensor version */ -	data = mt9v032_read(client, MT9V032_CHIP_VERSION); -	if (data != MT9V032_CHIP_ID_REV1 && data != MT9V032_CHIP_ID_REV3) { -		dev_err(&client->dev, "MT9V032 not detected, wrong version " -				"0x%04x\n", data); +	version = mt9v032_read(client, MT9V032_CHIP_VERSION); +	if (version < 0) { +		dev_err(&client->dev, "Failed reading chip version\n"); +		return version; +	} + +	for (i = 0; i < ARRAY_SIZE(mt9v032_versions); ++i) { +		if (mt9v032_versions[i].version == version) { +			mt9v032->version = &mt9v032_versions[i]; +			break; +		} +	} + +	if (mt9v032->version == NULL) { +		dev_err(&client->dev, "Unsupported chip version 0x%04x\n", +			version);  		return -ENODEV;  	}  	mt9v032_power_off(mt9v032); -	dev_info(&client->dev, "MT9V032 detected at address 0x%02x\n", -			client->addr); +	dev_info(&client->dev, "%s detected at address 0x%02x\n", +		 mt9v032->version->name, client->addr); -	mt9v032_configure_pixel_rate(mt9v032, 1); +	mt9v032_configure_pixel_rate(mt9v032);  	return ret;  }  static int mt9v032_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)  { +	struct mt9v032 *mt9v032 = to_mt9v032(subdev);  	struct v4l2_mbus_framefmt *format;  	struct v4l2_rect *crop; @@ -683,7 +819,12 @@ static int mt9v032_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)  	crop->height = MT9V032_WINDOW_HEIGHT_DEF;  	format = v4l2_subdev_get_try_format(fh, 0); -	format->code = V4L2_MBUS_FMT_SGRBG10_1X10; + +	if (mt9v032->model->color) +		format->code = V4L2_MBUS_FMT_SGRBG10_1X10; +	else +		format->code = V4L2_MBUS_FMT_Y10_1X10; +  	format->width = MT9V032_WINDOW_WIDTH_DEF;  	format->height = MT9V032_WINDOW_HEIGHT_DEF;  	format->field = V4L2_FIELD_NONE; @@ -755,6 +896,7 @@ static int mt9v032_probe(struct i2c_client *client,  	mutex_init(&mt9v032->power_lock);  	mt9v032->pdata = pdata; +	mt9v032->model = (const void *)did->driver_data;  	v4l2_ctrl_handler_init(&mt9v032->ctrls, 10); @@ -767,16 +909,16 @@ static int mt9v032_probe(struct i2c_client *client,  			       V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0,  			       V4L2_EXPOSURE_AUTO);  	v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, -			  V4L2_CID_EXPOSURE, MT9V032_TOTAL_SHUTTER_WIDTH_MIN, -			  MT9V032_TOTAL_SHUTTER_WIDTH_MAX, 1, +			  V4L2_CID_EXPOSURE, mt9v032->model->data->min_shutter, +			  mt9v032->model->data->max_shutter, 1,  			  MT9V032_TOTAL_SHUTTER_WIDTH_DEF);  	v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, -			  V4L2_CID_HBLANK, MT9V032_HORIZONTAL_BLANKING_MIN, +			  V4L2_CID_HBLANK, mt9v032->model->data->min_hblank,  			  MT9V032_HORIZONTAL_BLANKING_MAX, 1,  			  MT9V032_HORIZONTAL_BLANKING_DEF);  	v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, -			  V4L2_CID_VBLANK, MT9V032_VERTICAL_BLANKING_MIN, -			  MT9V032_VERTICAL_BLANKING_MAX, 1, +			  V4L2_CID_VBLANK, mt9v032->model->data->min_vblank, +			  mt9v032->model->data->max_vblank, 1,  			  MT9V032_VERTICAL_BLANKING_DEF);  	mt9v032->test_pattern = v4l2_ctrl_new_std_menu_items(&mt9v032->ctrls,  				&mt9v032_ctrl_ops, V4L2_CID_TEST_PATTERN, @@ -819,12 +961,19 @@ static int mt9v032_probe(struct i2c_client *client,  	mt9v032->crop.width = MT9V032_WINDOW_WIDTH_DEF;  	mt9v032->crop.height = MT9V032_WINDOW_HEIGHT_DEF; -	mt9v032->format.code = V4L2_MBUS_FMT_SGRBG10_1X10; +	if (mt9v032->model->color) +		mt9v032->format.code = V4L2_MBUS_FMT_SGRBG10_1X10; +	else +		mt9v032->format.code = V4L2_MBUS_FMT_Y10_1X10; +  	mt9v032->format.width = MT9V032_WINDOW_WIDTH_DEF;  	mt9v032->format.height = MT9V032_WINDOW_HEIGHT_DEF;  	mt9v032->format.field = V4L2_FIELD_NONE;  	mt9v032->format.colorspace = V4L2_COLORSPACE_SRGB; +	mt9v032->hratio = 1; +	mt9v032->vratio = 1; +  	mt9v032->aec_agc = MT9V032_AEC_ENABLE | MT9V032_AGC_ENABLE;  	mt9v032->hblank = MT9V032_HORIZONTAL_BLANKING_DEF;  	mt9v032->sysclk = MT9V032_SYSCLK_FREQ_DEF; @@ -855,7 +1004,10 @@ static int mt9v032_remove(struct i2c_client *client)  }  static const struct i2c_device_id mt9v032_id[] = { -	{ "mt9v032", 0 }, +	{ "mt9v032", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V032_COLOR] }, +	{ "mt9v032m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V032_MONO] }, +	{ "mt9v034", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V034_COLOR] }, +	{ "mt9v034m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V034_MONO] },  	{ }  };  MODULE_DEVICE_TABLE(i2c, mt9v032_id); diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index e8a1ce20403..cdd7c1b7259 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -1109,7 +1109,7 @@ static int ov7670_enum_framesizes(struct v4l2_subdev *sd,  	 * windows that fall outside that.  	 */  	for (i = 0; i < n_win_sizes; i++) { -		struct ov7670_win_size *win = &info->devtype->win_sizes[index]; +		struct ov7670_win_size *win = &info->devtype->win_sizes[i];  		if (info->min_width && win->width < info->min_width)  			continue;  		if (info->min_height && win->height < info->min_height) diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index b76ec0e7e68..ee0f57e01b5 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -15,7 +15,7 @@   * GNU General Public License for more details.   */ -#include <linux/sizes.h> +#include <linux/clk.h>  #include <linux/delay.h>  #include <linux/firmware.h>  #include <linux/gpio.h> @@ -23,7 +23,9 @@  #include <linux/init.h>  #include <linux/media.h>  #include <linux/module.h> +#include <linux/of_gpio.h>  #include <linux/regulator/consumer.h> +#include <linux/sizes.h>  #include <linux/slab.h>  #include <linux/spi/spi.h>  #include <linux/videodev2.h> @@ -33,6 +35,7 @@  #include <media/v4l2-subdev.h>  #include <media/v4l2-mediabus.h>  #include <media/s5c73m3.h> +#include <media/v4l2-of.h>  #include "s5c73m3.h" @@ -46,6 +49,8 @@ static int update_fw;  module_param(update_fw, int, 0644);  #define S5C73M3_EMBEDDED_DATA_MAXLEN	SZ_4K +#define S5C73M3_MIPI_DATA_LANES		4 +#define S5C73M3_CLK_NAME		"cis_extclk"  static const char * const s5c73m3_supply_names[S5C73M3_MAX_SUPPLIES] = {  	"vdd-int",	/* Digital Core supply (1.2V), CAM_ISP_CORE_1.2V */ @@ -1355,9 +1360,20 @@ static int __s5c73m3_power_on(struct s5c73m3 *state)  	for (i = 0; i < S5C73M3_MAX_SUPPLIES; i++) {  		ret = regulator_enable(state->supplies[i].consumer);  		if (ret) -			goto err; +			goto err_reg_dis;  	} +	ret = clk_set_rate(state->clock, state->mclk_frequency); +	if (ret < 0) +		goto err_reg_dis; + +	ret = clk_prepare_enable(state->clock); +	if (ret < 0) +		goto err_reg_dis; + +	v4l2_dbg(1, s5c73m3_dbg, &state->oif_sd, "clock frequency: %ld\n", +					clk_get_rate(state->clock)); +  	s5c73m3_gpio_deassert(state, STBY);  	usleep_range(100, 200); @@ -1365,7 +1381,8 @@ static int __s5c73m3_power_on(struct s5c73m3 *state)  	usleep_range(50, 100);  	return 0; -err: + +err_reg_dis:  	for (--i; i >= 0; i--)  		regulator_disable(state->supplies[i].consumer);  	return ret; @@ -1380,6 +1397,9 @@ static int __s5c73m3_power_off(struct s5c73m3 *state)  	if (s5c73m3_gpio_assert(state, STBY))  		usleep_range(100, 200); + +	clk_disable_unprepare(state->clock); +  	state->streaming = 0;  	state->isp_ready = 0; @@ -1388,6 +1408,7 @@ static int __s5c73m3_power_off(struct s5c73m3 *state)  		if (ret)  			goto err;  	} +  	return 0;  err:  	for (++i; i < S5C73M3_MAX_SUPPLIES; i++) { @@ -1396,6 +1417,8 @@ err:  			v4l2_err(&state->oif_sd, "Failed to reenable %s: %d\n",  				 state->supplies[i].supply, r);  	} + +	clk_prepare_enable(state->clock);  	return ret;  } @@ -1451,17 +1474,6 @@ static int s5c73m3_oif_registered(struct v4l2_subdev *sd)  			S5C73M3_JPEG_PAD, &state->oif_sd.entity, OIF_JPEG_PAD,  			MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); -	mutex_lock(&state->lock); -	ret = __s5c73m3_power_on(state); -	if (ret == 0) -		s5c73m3_get_fw_version(state); - -	__s5c73m3_power_off(state); -	mutex_unlock(&state->lock); - -	v4l2_dbg(1, s5c73m3_dbg, sd, "%s: Booting %s (%d)\n", -		 __func__, ret ? "failed" : "succeded", ret); -  	return ret;  } @@ -1519,41 +1531,112 @@ static const struct v4l2_subdev_ops oif_subdev_ops = {  	.video	= &s5c73m3_oif_video_ops,  }; -static int s5c73m3_configure_gpios(struct s5c73m3 *state, -				   const struct s5c73m3_platform_data *pdata) +static int s5c73m3_configure_gpios(struct s5c73m3 *state) +{ +	static const char * const gpio_names[] = { +		"S5C73M3_STBY", "S5C73M3_RST" +	}; +	struct i2c_client *c = state->i2c_client; +	struct s5c73m3_gpio *g = state->gpio; +	int ret, i; + +	for (i = 0; i < GPIO_NUM; ++i) { +		unsigned int flags = GPIOF_DIR_OUT; +		if (g[i].level) +			flags |= GPIOF_INIT_HIGH; +		ret = devm_gpio_request_one(&c->dev, g[i].gpio, flags, +					    gpio_names[i]); +		if (ret) { +			v4l2_err(c, "failed to request gpio %s\n", +				 gpio_names[i]); +			return ret; +		} +	} +	return 0; +} + +static int s5c73m3_parse_gpios(struct s5c73m3 *state) +{ +	static const char * const prop_names[] = { +		"standby-gpios", "xshutdown-gpios", +	}; +	struct device *dev = &state->i2c_client->dev; +	struct device_node *node = dev->of_node; +	int ret, i; + +	for (i = 0; i < GPIO_NUM; ++i) { +		enum of_gpio_flags of_flags; + +		ret = of_get_named_gpio_flags(node, prop_names[i], +					      0, &of_flags); +		if (ret < 0) { +			dev_err(dev, "failed to parse %s DT property\n", +				prop_names[i]); +			return -EINVAL; +		} +		state->gpio[i].gpio = ret; +		state->gpio[i].level = !(of_flags & OF_GPIO_ACTIVE_LOW); +	} +	return 0; +} + +static int s5c73m3_get_platform_data(struct s5c73m3 *state)  {  	struct device *dev = &state->i2c_client->dev; -	const struct s5c73m3_gpio *gpio; -	unsigned long flags; +	const struct s5c73m3_platform_data *pdata = dev->platform_data; +	struct device_node *node = dev->of_node; +	struct device_node *node_ep; +	struct v4l2_of_endpoint ep;  	int ret; -	state->gpio[STBY].gpio = -EINVAL; -	state->gpio[RST].gpio  = -EINVAL; +	if (!node) { +		if (!pdata) { +			dev_err(dev, "Platform data not specified\n"); +			return -EINVAL; +		} -	gpio = &pdata->gpio_stby; -	if (gpio_is_valid(gpio->gpio)) { -		flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW) -		      | GPIOF_EXPORT; -		ret = devm_gpio_request_one(dev, gpio->gpio, flags, -					    "S5C73M3_STBY"); -		if (ret < 0) -			return ret; +		state->mclk_frequency = pdata->mclk_frequency; +		state->gpio[STBY] = pdata->gpio_stby; +		state->gpio[RST] = pdata->gpio_reset; +		return 0; +	} + +	state->clock = devm_clk_get(dev, S5C73M3_CLK_NAME); +	if (IS_ERR(state->clock)) +		return PTR_ERR(state->clock); -		state->gpio[STBY] = *gpio; +	if (of_property_read_u32(node, "clock-frequency", +				 &state->mclk_frequency)) { +		state->mclk_frequency = S5C73M3_DEFAULT_MCLK_FREQ; +		dev_info(dev, "using default %u Hz clock frequency\n", +					state->mclk_frequency);  	} -	gpio = &pdata->gpio_reset; -	if (gpio_is_valid(gpio->gpio)) { -		flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW) -		      | GPIOF_EXPORT; -		ret = devm_gpio_request_one(dev, gpio->gpio, flags, -					    "S5C73M3_RST"); -		if (ret < 0) -			return ret; +	ret = s5c73m3_parse_gpios(state); +	if (ret < 0) +		return -EINVAL; -		state->gpio[RST] = *gpio; +	node_ep = of_graph_get_next_endpoint(node, NULL); +	if (!node_ep) { +		dev_warn(dev, "no endpoint defined for node: %s\n", +						node->full_name); +		return 0;  	} +	v4l2_of_parse_endpoint(node_ep, &ep); +	of_node_put(node_ep); + +	if (ep.bus_type != V4L2_MBUS_CSI2) { +		dev_err(dev, "unsupported bus type\n"); +		return -EINVAL; +	} +	/* +	 * Number of MIPI CSI-2 data lanes is currently not configurable, +	 * always a default value of 4 lanes is used. +	 */ +	if (ep.bus.mipi_csi2.num_data_lanes != S5C73M3_MIPI_DATA_LANES) +		dev_info(dev, "falling back to 4 MIPI CSI-2 data lanes\n"); +  	return 0;  } @@ -1561,27 +1644,26 @@ static int s5c73m3_probe(struct i2c_client *client,  				const struct i2c_device_id *id)  {  	struct device *dev = &client->dev; -	const struct s5c73m3_platform_data *pdata = client->dev.platform_data;  	struct v4l2_subdev *sd;  	struct v4l2_subdev *oif_sd;  	struct s5c73m3 *state;  	int ret, i; -	if (pdata == NULL) { -		dev_err(&client->dev, "Platform data not specified\n"); -		return -EINVAL; -	} -  	state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);  	if (!state)  		return -ENOMEM; +	state->i2c_client = client; +	ret = s5c73m3_get_platform_data(state); +	if (ret < 0) +		return ret; +  	mutex_init(&state->lock);  	sd = &state->sensor_sd;  	oif_sd = &state->oif_sd;  	v4l2_subdev_init(sd, &s5c73m3_subdev_ops); -	sd->owner = client->driver->driver.owner; +	sd->owner = client->dev.driver->owner;  	v4l2_set_subdevdata(sd, state);  	strlcpy(sd->name, "S5C73M3", sizeof(sd->name)); @@ -1613,11 +1695,7 @@ static int s5c73m3_probe(struct i2c_client *client,  	if (ret < 0)  		return ret; -	state->mclk_frequency = pdata->mclk_frequency; -	state->bus_type = pdata->bus_type; -	state->i2c_client = client; - -	ret = s5c73m3_configure_gpios(state, pdata); +	ret = s5c73m3_configure_gpios(state);  	if (ret)  		goto out_err; @@ -1651,9 +1729,29 @@ static int s5c73m3_probe(struct i2c_client *client,  	if (ret < 0)  		goto out_err; -	v4l2_info(sd, "%s: completed succesfully\n", __func__); +	oif_sd->dev = dev; + +	ret = __s5c73m3_power_on(state); +	if (ret < 0) +		goto out_err1; + +	ret = s5c73m3_get_fw_version(state); +	__s5c73m3_power_off(state); + +	if (ret < 0) { +		dev_err(dev, "Device detection failed: %d\n", ret); +		goto out_err1; +	} + +	ret = v4l2_async_register_subdev(oif_sd); +	if (ret < 0) +		goto out_err1; + +	v4l2_info(sd, "%s: completed successfully\n", __func__);  	return 0; +out_err1: +	s5c73m3_unregister_spi_driver(state);  out_err:  	media_entity_cleanup(&sd->entity);  	return ret; @@ -1665,7 +1763,7 @@ static int s5c73m3_remove(struct i2c_client *client)  	struct s5c73m3 *state = oif_sd_to_s5c73m3(oif_sd);  	struct v4l2_subdev *sensor_sd = &state->sensor_sd; -	v4l2_device_unregister_subdev(oif_sd); +	v4l2_async_unregister_subdev(oif_sd);  	v4l2_ctrl_handler_free(oif_sd->ctrl_handler);  	media_entity_cleanup(&oif_sd->entity); @@ -1684,8 +1782,17 @@ static const struct i2c_device_id s5c73m3_id[] = {  };  MODULE_DEVICE_TABLE(i2c, s5c73m3_id); +#ifdef CONFIG_OF +static const struct of_device_id s5c73m3_of_match[] = { +	{ .compatible = "samsung,s5c73m3" }, +	{ } +}; +MODULE_DEVICE_TABLE(of, s5c73m3_of_match); +#endif +  static struct i2c_driver s5c73m3_i2c_driver = {  	.driver = { +		.of_match_table = of_match_ptr(s5c73m3_of_match),  		.name	= DRIVER_NAME,  	},  	.probe		= s5c73m3_probe, diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c index 8079e26eb5e..f60b265b4da 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c @@ -27,6 +27,11 @@  #define S5C73M3_SPI_DRV_NAME "S5C73M3-SPI" +static const struct of_device_id s5c73m3_spi_ids[] = { +	{ .compatible = "samsung,s5c73m3" }, +	{ } +}; +  enum spi_direction {  	SPI_DIR_RX,  	SPI_DIR_TX @@ -146,6 +151,7 @@ int s5c73m3_register_spi_driver(struct s5c73m3 *state)  	spidrv->driver.name = S5C73M3_SPI_DRV_NAME;  	spidrv->driver.bus = &spi_bus_type;  	spidrv->driver.owner = THIS_MODULE; +	spidrv->driver.of_match_table = s5c73m3_spi_ids;  	return spi_register_driver(spidrv);  } diff --git a/drivers/media/i2c/s5c73m3/s5c73m3.h b/drivers/media/i2c/s5c73m3/s5c73m3.h index 9d2c0865224..9656b6723dc 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3.h +++ b/drivers/media/i2c/s5c73m3/s5c73m3.h @@ -17,6 +17,7 @@  #ifndef S5C73M3_H_  #define S5C73M3_H_ +#include <linux/clk.h>  #include <linux/kernel.h>  #include <linux/regulator/consumer.h>  #include <media/v4l2-common.h> @@ -321,6 +322,7 @@ enum s5c73m3_oif_pads {  #define S5C73M3_MAX_SUPPLIES			6 +#define S5C73M3_DEFAULT_MCLK_FREQ		24000000U  struct s5c73m3_ctrls {  	struct v4l2_ctrl_handler handler; @@ -391,9 +393,11 @@ struct s5c73m3 {  	struct regulator_bulk_data supplies[S5C73M3_MAX_SUPPLIES];  	struct s5c73m3_gpio gpio[GPIO_NUM]; +	struct clk *clock; +  	/* External master clock frequency */  	u32 mclk_frequency; -	/* Video bus type - MIPI-CSI2/paralell */ +	/* Video bus type - MIPI-CSI2/parallel */  	enum v4l2_mbus_type bus_type;  	const struct s5c73m3_frame_size *sensor_pix_size[2]; diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c new file mode 100644 index 00000000000..2d768ef67cc --- /dev/null +++ b/drivers/media/i2c/s5k5baf.c @@ -0,0 +1,2054 @@ +/* + * Driver for Samsung S5K5BAF UXGA 1/5" 2M CMOS Image Sensor + * with embedded SoC ISP. + * + * Copyright (C) 2013, Samsung Electronics Co., Ltd. + * Andrzej Hajda <a.hajda@samsung.com> + * + * Based on S5K6AA driver authored by Sylwester Nawrocki + * Copyright (C) 2013, Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/media.h> +#include <linux/module.h> +#include <linux/of_gpio.h> +#include <linux/of_graph.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> + +#include <media/media-entity.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-subdev.h> +#include <media/v4l2-mediabus.h> +#include <media/v4l2-of.h> + +static int debug; +module_param(debug, int, 0644); + +#define S5K5BAF_DRIVER_NAME		"s5k5baf" +#define S5K5BAF_DEFAULT_MCLK_FREQ	24000000U +#define S5K5BAF_CLK_NAME		"mclk" + +#define S5K5BAF_FW_FILENAME		"s5k5baf-cfg.bin" +#define S5K5BAF_FW_TAG			"SF00" +#define S5K5BAG_FW_TAG_LEN		2 +#define S5K5BAG_FW_MAX_COUNT		16 + +#define S5K5BAF_CIS_WIDTH		1600 +#define S5K5BAF_CIS_HEIGHT		1200 +#define S5K5BAF_WIN_WIDTH_MIN		8 +#define S5K5BAF_WIN_HEIGHT_MIN		8 +#define S5K5BAF_GAIN_RED_DEF		127 +#define S5K5BAF_GAIN_GREEN_DEF		95 +#define S5K5BAF_GAIN_BLUE_DEF		180 +/* Default number of MIPI CSI-2 data lanes used */ +#define S5K5BAF_DEF_NUM_LANES		1 + +#define AHB_MSB_ADDR_PTR		0xfcfc + +/* + * Register interface pages (the most significant word of the address) + */ +#define PAGE_IF_HW			0xd000 +#define PAGE_IF_SW			0x7000 + +/* + * H/W register Interface (PAGE_IF_HW) + */ +#define REG_SW_LOAD_COMPLETE		0x0014 +#define REG_CMDWR_PAGE			0x0028 +#define REG_CMDWR_ADDR			0x002a +#define REG_CMDRD_PAGE			0x002c +#define REG_CMDRD_ADDR			0x002e +#define REG_CMD_BUF			0x0f12 +#define REG_SET_HOST_INT		0x1000 +#define REG_CLEAR_HOST_INT		0x1030 +#define REG_PATTERN_SET			0x3100 +#define REG_PATTERN_WIDTH		0x3118 +#define REG_PATTERN_HEIGHT		0x311a +#define REG_PATTERN_PARAM		0x311c + +/* + * S/W register interface (PAGE_IF_SW) + */ + +/* Firmware revision information */ +#define REG_FW_APIVER			0x012e +#define  S5K5BAF_FW_APIVER		0x0001 +#define REG_FW_REVISION			0x0130 +#define REG_FW_SENSOR_ID		0x0152 + +/* Initialization parameters */ +/* Master clock frequency in KHz */ +#define REG_I_INCLK_FREQ_L		0x01b8 +#define REG_I_INCLK_FREQ_H		0x01ba +#define  MIN_MCLK_FREQ_KHZ		6000U +#define  MAX_MCLK_FREQ_KHZ		48000U +#define REG_I_USE_NPVI_CLOCKS		0x01c6 +#define  NPVI_CLOCKS			1 +#define REG_I_USE_NMIPI_CLOCKS		0x01c8 +#define  NMIPI_CLOCKS			1 +#define REG_I_BLOCK_INTERNAL_PLL_CALC	0x01ca + +/* Clock configurations, n = 0..2. REG_I_* frequency unit is 4 kHz. */ +#define REG_I_OPCLK_4KHZ(n)		((n) * 6 + 0x01cc) +#define REG_I_MIN_OUTRATE_4KHZ(n)	((n) * 6 + 0x01ce) +#define REG_I_MAX_OUTRATE_4KHZ(n)	((n) * 6 + 0x01d0) +#define  SCLK_PVI_FREQ			24000 +#define  SCLK_MIPI_FREQ			48000 +#define  PCLK_MIN_FREQ			6000 +#define  PCLK_MAX_FREQ			48000 +#define REG_I_USE_REGS_API		0x01de +#define REG_I_INIT_PARAMS_UPDATED	0x01e0 +#define REG_I_ERROR_INFO		0x01e2 + +/* General purpose parameters */ +#define REG_USER_BRIGHTNESS		0x01e4 +#define REG_USER_CONTRAST		0x01e6 +#define REG_USER_SATURATION		0x01e8 +#define REG_USER_SHARPBLUR		0x01ea + +#define REG_G_SPEC_EFFECTS		0x01ee +#define REG_G_ENABLE_PREV		0x01f0 +#define REG_G_ENABLE_PREV_CHG		0x01f2 +#define REG_G_NEW_CFG_SYNC		0x01f8 +#define REG_G_PREVREQ_IN_WIDTH		0x01fa +#define REG_G_PREVREQ_IN_HEIGHT		0x01fc +#define REG_G_PREVREQ_IN_XOFFS		0x01fe +#define REG_G_PREVREQ_IN_YOFFS		0x0200 +#define REG_G_PREVZOOM_IN_WIDTH		0x020a +#define REG_G_PREVZOOM_IN_HEIGHT	0x020c +#define REG_G_PREVZOOM_IN_XOFFS		0x020e +#define REG_G_PREVZOOM_IN_YOFFS		0x0210 +#define REG_G_INPUTS_CHANGE_REQ		0x021a +#define REG_G_ACTIVE_PREV_CFG		0x021c +#define REG_G_PREV_CFG_CHG		0x021e +#define REG_G_PREV_OPEN_AFTER_CH	0x0220 +#define REG_G_PREV_CFG_ERROR		0x0222 +#define  CFG_ERROR_RANGE		0x0b +#define REG_G_PREV_CFG_BYPASS_CHANGED	0x022a +#define REG_G_ACTUAL_P_FR_TIME		0x023a +#define REG_G_ACTUAL_P_OUT_RATE		0x023c +#define REG_G_ACTUAL_C_FR_TIME		0x023e +#define REG_G_ACTUAL_C_OUT_RATE		0x0240 + +/* Preview control section. n = 0...4. */ +#define PREG(n, x)			((n) * 0x26 + x) +#define REG_P_OUT_WIDTH(n)		PREG(n, 0x0242) +#define REG_P_OUT_HEIGHT(n)		PREG(n, 0x0244) +#define REG_P_FMT(n)			PREG(n, 0x0246) +#define REG_P_MAX_OUT_RATE(n)		PREG(n, 0x0248) +#define REG_P_MIN_OUT_RATE(n)		PREG(n, 0x024a) +#define REG_P_PVI_MASK(n)		PREG(n, 0x024c) +#define  PVI_MASK_MIPI			0x52 +#define REG_P_CLK_INDEX(n)		PREG(n, 0x024e) +#define  CLK_PVI_INDEX			0 +#define  CLK_MIPI_INDEX			NPVI_CLOCKS +#define REG_P_FR_RATE_TYPE(n)		PREG(n, 0x0250) +#define  FR_RATE_DYNAMIC		0 +#define  FR_RATE_FIXED			1 +#define  FR_RATE_FIXED_ACCURATE		2 +#define REG_P_FR_RATE_Q_TYPE(n)		PREG(n, 0x0252) +#define  FR_RATE_Q_DYNAMIC		0 +#define  FR_RATE_Q_BEST_FRRATE		1 /* Binning enabled */ +#define  FR_RATE_Q_BEST_QUALITY		2 /* Binning disabled */ +/* Frame period in 0.1 ms units */ +#define REG_P_MAX_FR_TIME(n)		PREG(n, 0x0254) +#define REG_P_MIN_FR_TIME(n)		PREG(n, 0x0256) +#define  S5K5BAF_MIN_FR_TIME		333  /* x100 us */ +#define  S5K5BAF_MAX_FR_TIME		6500 /* x100 us */ +/* The below 5 registers are for "device correction" values */ +#define REG_P_SATURATION(n)		PREG(n, 0x0258) +#define REG_P_SHARP_BLUR(n)		PREG(n, 0x025a) +#define REG_P_GLAMOUR(n)		PREG(n, 0x025c) +#define REG_P_COLORTEMP(n)		PREG(n, 0x025e) +#define REG_P_GAMMA_INDEX(n)		PREG(n, 0x0260) +#define REG_P_PREV_MIRROR(n)		PREG(n, 0x0262) +#define REG_P_CAP_MIRROR(n)		PREG(n, 0x0264) +#define REG_P_CAP_ROTATION(n)		PREG(n, 0x0266) + +/* Extended image property controls */ +/* Exposure time in 10 us units */ +#define REG_SF_USR_EXPOSURE_L		0x03bc +#define REG_SF_USR_EXPOSURE_H		0x03be +#define REG_SF_USR_EXPOSURE_CHG		0x03c0 +#define REG_SF_USR_TOT_GAIN		0x03c2 +#define REG_SF_USR_TOT_GAIN_CHG		0x03c4 +#define REG_SF_RGAIN			0x03c6 +#define REG_SF_RGAIN_CHG		0x03c8 +#define REG_SF_GGAIN			0x03ca +#define REG_SF_GGAIN_CHG		0x03cc +#define REG_SF_BGAIN			0x03ce +#define REG_SF_BGAIN_CHG		0x03d0 +#define REG_SF_WBGAIN_CHG		0x03d2 +#define REG_SF_FLICKER_QUANT		0x03d4 +#define REG_SF_FLICKER_QUANT_CHG	0x03d6 + +/* Output interface (parallel/MIPI) setup */ +#define REG_OIF_EN_MIPI_LANES		0x03f2 +#define REG_OIF_EN_PACKETS		0x03f4 +#define  EN_PACKETS_CSI2		0xc3 +#define REG_OIF_CFG_CHG			0x03f6 + +/* Auto-algorithms enable mask */ +#define REG_DBG_AUTOALG_EN		0x03f8 +#define  AALG_ALL_EN			BIT(0) +#define  AALG_AE_EN			BIT(1) +#define  AALG_DIVLEI_EN			BIT(2) +#define  AALG_WB_EN			BIT(3) +#define  AALG_USE_WB_FOR_ISP		BIT(4) +#define  AALG_FLICKER_EN		BIT(5) +#define  AALG_FIT_EN			BIT(6) +#define  AALG_WRHW_EN			BIT(7) + +/* Pointers to color correction matrices */ +#define REG_PTR_CCM_HORIZON		0x06d0 +#define REG_PTR_CCM_INCANDESCENT	0x06d4 +#define REG_PTR_CCM_WARM_WHITE		0x06d8 +#define REG_PTR_CCM_COOL_WHITE		0x06dc +#define REG_PTR_CCM_DL50		0x06e0 +#define REG_PTR_CCM_DL65		0x06e4 +#define REG_PTR_CCM_OUTDOOR		0x06ec + +#define REG_ARR_CCM(n)			(0x2800 + 36 * (n)) + +static const char * const s5k5baf_supply_names[] = { +	"vdda",		/* Analog power supply 2.8V (2.6V to 3.0V) */ +	"vddreg",	/* Regulator input power supply 1.8V (1.7V to 1.9V) +			   or 2.8V (2.6V to 3.0) */ +	"vddio",	/* I/O power supply 1.8V (1.65V to 1.95V) +			   or 2.8V (2.5V to 3.1V) */ +}; +#define S5K5BAF_NUM_SUPPLIES ARRAY_SIZE(s5k5baf_supply_names) + +struct s5k5baf_gpio { +	int gpio; +	int level; +}; + +enum s5k5baf_gpio_id { +	STBY, +	RST, +	NUM_GPIOS, +}; + +#define PAD_CIS 0 +#define PAD_OUT 1 +#define NUM_CIS_PADS 1 +#define NUM_ISP_PADS 2 + +struct s5k5baf_pixfmt { +	enum v4l2_mbus_pixelcode code; +	u32 colorspace; +	/* REG_P_FMT(x) register value */ +	u16 reg_p_fmt; +}; + +struct s5k5baf_ctrls { +	struct v4l2_ctrl_handler handler; +	struct { /* Auto / manual white balance cluster */ +		struct v4l2_ctrl *awb; +		struct v4l2_ctrl *gain_red; +		struct v4l2_ctrl *gain_blue; +	}; +	struct { /* Mirror cluster */ +		struct v4l2_ctrl *hflip; +		struct v4l2_ctrl *vflip; +	}; +	struct { /* Auto exposure / manual exposure and gain cluster */ +		struct v4l2_ctrl *auto_exp; +		struct v4l2_ctrl *exposure; +		struct v4l2_ctrl *gain; +	}; +}; + +enum { +	S5K5BAF_FW_ID_PATCH, +	S5K5BAF_FW_ID_CCM, +	S5K5BAF_FW_ID_CIS, +}; + +struct s5k5baf_fw { +	u16 count; +	struct { +		u16 id; +		u16 offset; +	} seq[0]; +	u16 data[0]; +}; + +struct s5k5baf { +	struct s5k5baf_gpio gpios[NUM_GPIOS]; +	enum v4l2_mbus_type bus_type; +	u8 nlanes; +	struct regulator_bulk_data supplies[S5K5BAF_NUM_SUPPLIES]; + +	struct clk *clock; +	u32 mclk_frequency; + +	struct s5k5baf_fw *fw; + +	struct v4l2_subdev cis_sd; +	struct media_pad cis_pad; + +	struct v4l2_subdev sd; +	struct media_pad pads[NUM_ISP_PADS]; + +	/* protects the struct members below */ +	struct mutex lock; + +	int error; + +	struct v4l2_rect crop_sink; +	struct v4l2_rect compose; +	struct v4l2_rect crop_source; +	/* index to s5k5baf_formats array */ +	int pixfmt; +	/* actual frame interval in 100us */ +	u16 fiv; +	/* requested frame interval in 100us */ +	u16 req_fiv; +	/* cache for REG_DBG_AUTOALG_EN register */ +	u16 auto_alg; + +	struct s5k5baf_ctrls ctrls; + +	unsigned int streaming:1; +	unsigned int apply_cfg:1; +	unsigned int apply_crop:1; +	unsigned int valid_auto_alg:1; +	unsigned int power; +}; + +static const struct s5k5baf_pixfmt s5k5baf_formats[] = { +	{ V4L2_MBUS_FMT_VYUY8_2X8,	V4L2_COLORSPACE_JPEG,	5 }, +	/* range 16-240 */ +	{ V4L2_MBUS_FMT_VYUY8_2X8,	V4L2_COLORSPACE_REC709,	6 }, +	{ V4L2_MBUS_FMT_RGB565_2X8_BE,	V4L2_COLORSPACE_JPEG,	0 }, +}; + +static struct v4l2_rect s5k5baf_cis_rect = { +	0, 0, S5K5BAF_CIS_WIDTH, S5K5BAF_CIS_HEIGHT +}; + +/* Setfile contains set of I2C command sequences. Each sequence has its ID. + * setfile format: + *	u8 magic[4]; + *	u16 count;		number of sequences + *	struct { + *		u16 id;		sequence id + *		u16 offset;	sequence offset in data array + *	} seq[count]; + *	u16 data[*];		array containing sequences + * + */ +static int s5k5baf_fw_parse(struct device *dev, struct s5k5baf_fw **fw, +			    size_t count, const u16 *data) +{ +	struct s5k5baf_fw *f; +	u16 *d, i, *end; +	int ret; + +	if (count < S5K5BAG_FW_TAG_LEN + 1) { +		dev_err(dev, "firmware file too short (%zu)\n", count); +		return -EINVAL; +	} + +	ret = memcmp(data, S5K5BAF_FW_TAG, S5K5BAG_FW_TAG_LEN * sizeof(u16)); +	if (ret != 0) { +		dev_err(dev, "invalid firmware magic number\n"); +		return -EINVAL; +	} + +	data += S5K5BAG_FW_TAG_LEN; +	count -= S5K5BAG_FW_TAG_LEN; + +	d = devm_kzalloc(dev, count * sizeof(u16), GFP_KERNEL); + +	for (i = 0; i < count; ++i) +		d[i] = le16_to_cpu(data[i]); + +	f = (struct s5k5baf_fw *)d; +	if (count < 1 + 2 * f->count) { +		dev_err(dev, "invalid firmware header (count=%d size=%zu)\n", +			f->count, 2 * (count + S5K5BAG_FW_TAG_LEN)); +		return -EINVAL; +	} +	end = d + count; +	d += 1 + 2 * f->count; + +	for (i = 0; i < f->count; ++i) { +		if (f->seq[i].offset + d <= end) +			continue; +		dev_err(dev, "invalid firmware header (seq=%d)\n", i); +		return -EINVAL; +	} + +	*fw = f; + +	return 0; +} + +static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) +{ +	return &container_of(ctrl->handler, struct s5k5baf, ctrls.handler)->sd; +} + +static inline bool s5k5baf_is_cis_subdev(struct v4l2_subdev *sd) +{ +	return sd->entity.type == MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; +} + +static inline struct s5k5baf *to_s5k5baf(struct v4l2_subdev *sd) +{ +	if (s5k5baf_is_cis_subdev(sd)) +		return container_of(sd, struct s5k5baf, cis_sd); +	else +		return container_of(sd, struct s5k5baf, sd); +} + +static u16 s5k5baf_i2c_read(struct s5k5baf *state, u16 addr) +{ +	struct i2c_client *c = v4l2_get_subdevdata(&state->sd); +	__be16 w, r; +	struct i2c_msg msg[] = { +		{ .addr = c->addr, .flags = 0, +		  .len = 2, .buf = (u8 *)&w }, +		{ .addr = c->addr, .flags = I2C_M_RD, +		  .len = 2, .buf = (u8 *)&r }, +	}; +	int ret; + +	if (state->error) +		return 0; + +	w = cpu_to_be16(addr); +	ret = i2c_transfer(c->adapter, msg, 2); +	r = be16_to_cpu(r); + +	v4l2_dbg(3, debug, c, "i2c_read: 0x%04x : 0x%04x\n", addr, r); + +	if (ret != 2) { +		v4l2_err(c, "i2c_read: error during transfer (%d)\n", ret); +		state->error = ret; +	} +	return r; +} + +static void s5k5baf_i2c_write(struct s5k5baf *state, u16 addr, u16 val) +{ +	u8 buf[4] = { addr >> 8, addr & 0xFF, val >> 8, val & 0xFF }; +	struct i2c_client *c = v4l2_get_subdevdata(&state->sd); +	int ret; + +	if (state->error) +		return; + +	ret = i2c_master_send(c, buf, 4); +	v4l2_dbg(3, debug, c, "i2c_write: 0x%04x : 0x%04x\n", addr, val); + +	if (ret != 4) { +		v4l2_err(c, "i2c_write: error during transfer (%d)\n", ret); +		state->error = ret; +	} +} + +static u16 s5k5baf_read(struct s5k5baf *state, u16 addr) +{ +	s5k5baf_i2c_write(state, REG_CMDRD_ADDR, addr); +	return s5k5baf_i2c_read(state, REG_CMD_BUF); +} + +static void s5k5baf_write(struct s5k5baf *state, u16 addr, u16 val) +{ +	s5k5baf_i2c_write(state, REG_CMDWR_ADDR, addr); +	s5k5baf_i2c_write(state, REG_CMD_BUF, val); +} + +static void s5k5baf_write_arr_seq(struct s5k5baf *state, u16 addr, +				  u16 count, const u16 *seq) +{ +	struct i2c_client *c = v4l2_get_subdevdata(&state->sd); +	__be16 buf[65]; + +	s5k5baf_i2c_write(state, REG_CMDWR_ADDR, addr); +	if (state->error) +		return; + +	v4l2_dbg(3, debug, c, "i2c_write_seq(count=%d): %*ph\n", count, +		 min(2 * count, 64), seq); + +	buf[0] = __constant_cpu_to_be16(REG_CMD_BUF); + +	while (count > 0) { +		int n = min_t(int, count, ARRAY_SIZE(buf) - 1); +		int ret, i; + +		for (i = 1; i <= n; ++i) +			buf[i] = cpu_to_be16(*seq++); + +		i *= 2; +		ret = i2c_master_send(c, (char *)buf, i); +		if (ret != i) { +			v4l2_err(c, "i2c_write_seq: error during transfer (%d)\n", ret); +			state->error = ret; +			break; +		} + +		count -= n; +	} +} + +#define s5k5baf_write_seq(state, addr, seq...) \ +	s5k5baf_write_arr_seq(state, addr, sizeof((char[]){ seq }), \ +			      (const u16 []){ seq }); + +/* add items count at the beginning of the list */ +#define NSEQ(seq...) sizeof((char[]){ seq }), seq + +/* + * s5k5baf_write_nseq() - Writes sequences of values to sensor memory via i2c + * @nseq: sequence of u16 words in format: + *	(N, address, value[1]...value[N-1])*,0 + * Ex.: + *	u16 seq[] = { NSEQ(0x4000, 1, 1), NSEQ(0x4010, 640, 480), 0 }; + *	ret = s5k5baf_write_nseq(c, seq); + */ +static void s5k5baf_write_nseq(struct s5k5baf *state, const u16 *nseq) +{ +	int count; + +	while ((count = *nseq++)) { +		u16 addr = *nseq++; +		--count; + +		s5k5baf_write_arr_seq(state, addr, count, nseq); +		nseq += count; +	} +} + +static void s5k5baf_synchronize(struct s5k5baf *state, int timeout, u16 addr) +{ +	unsigned long end = jiffies + msecs_to_jiffies(timeout); +	u16 reg; + +	s5k5baf_write(state, addr, 1); +	do { +		reg = s5k5baf_read(state, addr); +		if (state->error || !reg) +			return; +		usleep_range(5000, 10000); +	} while (time_is_after_jiffies(end)); + +	v4l2_err(&state->sd, "timeout on register synchronize (%#x)\n", addr); +	state->error = -ETIMEDOUT; +} + +static u16 *s5k5baf_fw_get_seq(struct s5k5baf *state, u16 seq_id) +{ +	struct s5k5baf_fw *fw = state->fw; +	u16 *data; +	int i; + +	if (fw == NULL) +		return NULL; + +	data = fw->data + 2 * fw->count; + +	for (i = 0; i < fw->count; ++i) { +		if (fw->seq[i].id == seq_id) +			return data + fw->seq[i].offset; +	} + +	return NULL; +} + +static void s5k5baf_hw_patch(struct s5k5baf *state) +{ +	u16 *seq = s5k5baf_fw_get_seq(state, S5K5BAF_FW_ID_PATCH); + +	if (seq) +		s5k5baf_write_nseq(state, seq); +} + +static void s5k5baf_hw_set_clocks(struct s5k5baf *state) +{ +	unsigned long mclk = state->mclk_frequency / 1000; +	u16 status; +	static const u16 nseq_clk_cfg[] = { +		NSEQ(REG_I_USE_NPVI_CLOCKS, +		  NPVI_CLOCKS, NMIPI_CLOCKS, 0, +		  SCLK_PVI_FREQ / 4, PCLK_MIN_FREQ / 4, PCLK_MAX_FREQ / 4, +		  SCLK_MIPI_FREQ / 4, PCLK_MIN_FREQ / 4, PCLK_MAX_FREQ / 4), +		NSEQ(REG_I_USE_REGS_API, 1), +		0 +	}; + +	s5k5baf_write_seq(state, REG_I_INCLK_FREQ_L, mclk & 0xffff, mclk >> 16); +	s5k5baf_write_nseq(state, nseq_clk_cfg); + +	s5k5baf_synchronize(state, 250, REG_I_INIT_PARAMS_UPDATED); +	status = s5k5baf_read(state, REG_I_ERROR_INFO); +	if (!state->error && status) { +		v4l2_err(&state->sd, "error configuring PLL (%d)\n", status); +		state->error = -EINVAL; +	} +} + +/* set custom color correction matrices for various illuminations */ +static void s5k5baf_hw_set_ccm(struct s5k5baf *state) +{ +	u16 *seq = s5k5baf_fw_get_seq(state, S5K5BAF_FW_ID_CCM); + +	if (seq) +		s5k5baf_write_nseq(state, seq); +} + +/* CIS sensor tuning, based on undocumented android driver code */ +static void s5k5baf_hw_set_cis(struct s5k5baf *state) +{ +	u16 *seq = s5k5baf_fw_get_seq(state, S5K5BAF_FW_ID_CIS); + +	if (!seq) +		return; + +	s5k5baf_i2c_write(state, REG_CMDWR_PAGE, PAGE_IF_HW); +	s5k5baf_write_nseq(state, seq); +	s5k5baf_i2c_write(state, REG_CMDWR_PAGE, PAGE_IF_SW); +} + +static void s5k5baf_hw_sync_cfg(struct s5k5baf *state) +{ +	s5k5baf_write(state, REG_G_PREV_CFG_CHG, 1); +	if (state->apply_crop) { +		s5k5baf_write(state, REG_G_INPUTS_CHANGE_REQ, 1); +		s5k5baf_write(state, REG_G_PREV_CFG_BYPASS_CHANGED, 1); +	} +	s5k5baf_synchronize(state, 500, REG_G_NEW_CFG_SYNC); +} +/* Set horizontal and vertical image flipping */ +static void s5k5baf_hw_set_mirror(struct s5k5baf *state) +{ +	u16 flip = state->ctrls.vflip->val | (state->ctrls.vflip->val << 1); + +	s5k5baf_write(state, REG_P_PREV_MIRROR(0), flip); +	if (state->streaming) +		s5k5baf_hw_sync_cfg(state); +} + +static void s5k5baf_hw_set_alg(struct s5k5baf *state, u16 alg, bool enable) +{ +	u16 cur_alg, new_alg; + +	if (!state->valid_auto_alg) +		cur_alg = s5k5baf_read(state, REG_DBG_AUTOALG_EN); +	else +		cur_alg = state->auto_alg; + +	new_alg = enable ? (cur_alg | alg) : (cur_alg & ~alg); + +	if (new_alg != cur_alg) +		s5k5baf_write(state, REG_DBG_AUTOALG_EN, new_alg); + +	if (state->error) +		return; + +	state->valid_auto_alg = 1; +	state->auto_alg = new_alg; +} + +/* Configure auto/manual white balance and R/G/B gains */ +static void s5k5baf_hw_set_awb(struct s5k5baf *state, int awb) +{ +	struct s5k5baf_ctrls *ctrls = &state->ctrls; + +	if (!awb) +		s5k5baf_write_seq(state, REG_SF_RGAIN, +				  ctrls->gain_red->val, 1, +				  S5K5BAF_GAIN_GREEN_DEF, 1, +				  ctrls->gain_blue->val, 1, +				  1); + +	s5k5baf_hw_set_alg(state, AALG_WB_EN, awb); +} + +/* Program FW with exposure time, 'exposure' in us units */ +static void s5k5baf_hw_set_user_exposure(struct s5k5baf *state, int exposure) +{ +	unsigned int time = exposure / 10; + +	s5k5baf_write_seq(state, REG_SF_USR_EXPOSURE_L, +			  time & 0xffff, time >> 16, 1); +} + +static void s5k5baf_hw_set_user_gain(struct s5k5baf *state, int gain) +{ +	s5k5baf_write_seq(state, REG_SF_USR_TOT_GAIN, gain, 1); +} + +/* Set auto/manual exposure and total gain */ +static void s5k5baf_hw_set_auto_exposure(struct s5k5baf *state, int value) +{ +	if (value == V4L2_EXPOSURE_AUTO) { +		s5k5baf_hw_set_alg(state, AALG_AE_EN | AALG_DIVLEI_EN, true); +	} else { +		unsigned int exp_time = state->ctrls.exposure->val; + +		s5k5baf_hw_set_user_exposure(state, exp_time); +		s5k5baf_hw_set_user_gain(state, state->ctrls.gain->val); +		s5k5baf_hw_set_alg(state, AALG_AE_EN | AALG_DIVLEI_EN, false); +	} +} + +static void s5k5baf_hw_set_anti_flicker(struct s5k5baf *state, int v) +{ +	if (v == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) { +		s5k5baf_hw_set_alg(state, AALG_FLICKER_EN, true); +	} else { +		/* The V4L2_CID_LINE_FREQUENCY control values match +		 * the register values */ +		s5k5baf_write_seq(state, REG_SF_FLICKER_QUANT, v, 1); +		s5k5baf_hw_set_alg(state, AALG_FLICKER_EN, false); +	} +} + +static void s5k5baf_hw_set_colorfx(struct s5k5baf *state, int val) +{ +	static const u16 colorfx[] = { +		[V4L2_COLORFX_NONE] = 0, +		[V4L2_COLORFX_BW] = 1, +		[V4L2_COLORFX_NEGATIVE] = 2, +		[V4L2_COLORFX_SEPIA] = 3, +		[V4L2_COLORFX_SKY_BLUE] = 4, +		[V4L2_COLORFX_SKETCH] = 5, +	}; + +	s5k5baf_write(state, REG_G_SPEC_EFFECTS, colorfx[val]); +} + +static int s5k5baf_find_pixfmt(struct v4l2_mbus_framefmt *mf) +{ +	int i, c = -1; + +	for (i = 0; i < ARRAY_SIZE(s5k5baf_formats); i++) { +		if (mf->colorspace != s5k5baf_formats[i].colorspace) +			continue; +		if (mf->code == s5k5baf_formats[i].code) +			return i; +		if (c < 0) +			c = i; +	} +	return (c < 0) ? 0 : c; +} + +static int s5k5baf_clear_error(struct s5k5baf *state) +{ +	int ret = state->error; + +	state->error = 0; +	return ret; +} + +static int s5k5baf_hw_set_video_bus(struct s5k5baf *state) +{ +	u16 en_pkts; + +	if (state->bus_type == V4L2_MBUS_CSI2) +		en_pkts = EN_PACKETS_CSI2; +	else +		en_pkts = 0; + +	s5k5baf_write_seq(state, REG_OIF_EN_MIPI_LANES, +			  state->nlanes, en_pkts, 1); + +	return s5k5baf_clear_error(state); +} + +static u16 s5k5baf_get_cfg_error(struct s5k5baf *state) +{ +	u16 err = s5k5baf_read(state, REG_G_PREV_CFG_ERROR); +	if (err) +		s5k5baf_write(state, REG_G_PREV_CFG_ERROR, 0); +	return err; +} + +static void s5k5baf_hw_set_fiv(struct s5k5baf *state, u16 fiv) +{ +	s5k5baf_write(state, REG_P_MAX_FR_TIME(0), fiv); +	s5k5baf_hw_sync_cfg(state); +} + +static void s5k5baf_hw_find_min_fiv(struct s5k5baf *state) +{ +	u16 err, fiv; +	int n; + +	fiv = s5k5baf_read(state,  REG_G_ACTUAL_P_FR_TIME); +	if (state->error) +		return; + +	for (n = 5; n > 0; --n) { +		s5k5baf_hw_set_fiv(state, fiv); +		err = s5k5baf_get_cfg_error(state); +		if (state->error) +			return; +		switch (err) { +		case CFG_ERROR_RANGE: +			++fiv; +			break; +		case 0: +			state->fiv = fiv; +			v4l2_info(&state->sd, +				  "found valid frame interval: %d00us\n", fiv); +			return; +		default: +			v4l2_err(&state->sd, +				 "error setting frame interval: %d\n", err); +			state->error = -EINVAL; +		} +	}; +	v4l2_err(&state->sd, "cannot find correct frame interval\n"); +	state->error = -ERANGE; +} + +static void s5k5baf_hw_validate_cfg(struct s5k5baf *state) +{ +	u16 err; + +	err = s5k5baf_get_cfg_error(state); +	if (state->error) +		return; + +	switch (err) { +	case 0: +		state->apply_cfg = 1; +		return; +	case CFG_ERROR_RANGE: +		s5k5baf_hw_find_min_fiv(state); +		if (!state->error) +			state->apply_cfg = 1; +		return; +	default: +		v4l2_err(&state->sd, +			 "error setting format: %d\n", err); +		state->error = -EINVAL; +	} +} + +static void s5k5baf_rescale(struct v4l2_rect *r, const struct v4l2_rect *v, +			    const struct v4l2_rect *n, +			    const struct v4l2_rect *d) +{ +	r->left = v->left * n->width / d->width; +	r->top = v->top * n->height / d->height; +	r->width = v->width * n->width / d->width; +	r->height = v->height * n->height / d->height; +} + +static int s5k5baf_hw_set_crop_rects(struct s5k5baf *state) +{ +	struct v4l2_rect *p, r; +	u16 err; +	int ret; + +	p = &state->crop_sink; +	s5k5baf_write_seq(state, REG_G_PREVREQ_IN_WIDTH, p->width, p->height, +			  p->left, p->top); + +	s5k5baf_rescale(&r, &state->crop_source, &state->crop_sink, +			&state->compose); +	s5k5baf_write_seq(state, REG_G_PREVZOOM_IN_WIDTH, r.width, r.height, +			  r.left, r.top); + +	s5k5baf_synchronize(state, 500, REG_G_INPUTS_CHANGE_REQ); +	s5k5baf_synchronize(state, 500, REG_G_PREV_CFG_BYPASS_CHANGED); +	err = s5k5baf_get_cfg_error(state); +	ret = s5k5baf_clear_error(state); +	if (ret < 0) +		return ret; + +	switch (err) { +	case 0: +		break; +	case CFG_ERROR_RANGE: +		/* retry crop with frame interval set to max */ +		s5k5baf_hw_set_fiv(state, S5K5BAF_MAX_FR_TIME); +		err = s5k5baf_get_cfg_error(state); +		ret = s5k5baf_clear_error(state); +		if (ret < 0) +			return ret; +		if (err) { +			v4l2_err(&state->sd, +				 "crop error on max frame interval: %d\n", err); +			state->error = -EINVAL; +		} +		s5k5baf_hw_set_fiv(state, state->req_fiv); +		s5k5baf_hw_validate_cfg(state); +		break; +	default: +		v4l2_err(&state->sd, "crop error: %d\n", err); +		return -EINVAL; +	} + +	if (!state->apply_cfg) +		return 0; + +	p = &state->crop_source; +	s5k5baf_write_seq(state, REG_P_OUT_WIDTH(0), p->width, p->height); +	s5k5baf_hw_set_fiv(state, state->req_fiv); +	s5k5baf_hw_validate_cfg(state); + +	return s5k5baf_clear_error(state); +} + +static void s5k5baf_hw_set_config(struct s5k5baf *state) +{ +	u16 reg_fmt = s5k5baf_formats[state->pixfmt].reg_p_fmt; +	struct v4l2_rect *r = &state->crop_source; + +	s5k5baf_write_seq(state, REG_P_OUT_WIDTH(0), +			  r->width, r->height, reg_fmt, +			  PCLK_MAX_FREQ >> 2, PCLK_MIN_FREQ >> 2, +			  PVI_MASK_MIPI, CLK_MIPI_INDEX, +			  FR_RATE_FIXED, FR_RATE_Q_DYNAMIC, +			  state->req_fiv, S5K5BAF_MIN_FR_TIME); +	s5k5baf_hw_sync_cfg(state); +	s5k5baf_hw_validate_cfg(state); +} + + +static void s5k5baf_hw_set_test_pattern(struct s5k5baf *state, int id) +{ +	s5k5baf_i2c_write(state, REG_PATTERN_WIDTH, 800); +	s5k5baf_i2c_write(state, REG_PATTERN_HEIGHT, 511); +	s5k5baf_i2c_write(state, REG_PATTERN_PARAM, 0); +	s5k5baf_i2c_write(state, REG_PATTERN_SET, id); +} + +static void s5k5baf_gpio_assert(struct s5k5baf *state, int id) +{ +	struct s5k5baf_gpio *gpio = &state->gpios[id]; + +	gpio_set_value(gpio->gpio, gpio->level); +} + +static void s5k5baf_gpio_deassert(struct s5k5baf *state, int id) +{ +	struct s5k5baf_gpio *gpio = &state->gpios[id]; + +	gpio_set_value(gpio->gpio, !gpio->level); +} + +static int s5k5baf_power_on(struct s5k5baf *state) +{ +	int ret; + +	ret = regulator_bulk_enable(S5K5BAF_NUM_SUPPLIES, state->supplies); +	if (ret < 0) +		goto err; + +	ret = clk_set_rate(state->clock, state->mclk_frequency); +	if (ret < 0) +		goto err_reg_dis; + +	ret = clk_prepare_enable(state->clock); +	if (ret < 0) +		goto err_reg_dis; + +	v4l2_dbg(1, debug, &state->sd, "clock frequency: %ld\n", +		 clk_get_rate(state->clock)); + +	s5k5baf_gpio_deassert(state, STBY); +	usleep_range(50, 100); +	s5k5baf_gpio_deassert(state, RST); +	return 0; + +err_reg_dis: +	regulator_bulk_disable(S5K5BAF_NUM_SUPPLIES, state->supplies); +err: +	v4l2_err(&state->sd, "%s() failed (%d)\n", __func__, ret); +	return ret; +} + +static int s5k5baf_power_off(struct s5k5baf *state) +{ +	int ret; + +	state->streaming = 0; +	state->apply_cfg = 0; +	state->apply_crop = 0; + +	s5k5baf_gpio_assert(state, RST); +	s5k5baf_gpio_assert(state, STBY); + +	if (!IS_ERR(state->clock)) +		clk_disable_unprepare(state->clock); + +	ret = regulator_bulk_disable(S5K5BAF_NUM_SUPPLIES, +					state->supplies); +	if (ret < 0) +		v4l2_err(&state->sd, "failed to disable regulators\n"); + +	return 0; +} + +static void s5k5baf_hw_init(struct s5k5baf *state) +{ +	s5k5baf_i2c_write(state, AHB_MSB_ADDR_PTR, PAGE_IF_HW); +	s5k5baf_i2c_write(state, REG_CLEAR_HOST_INT, 0); +	s5k5baf_i2c_write(state, REG_SW_LOAD_COMPLETE, 1); +	s5k5baf_i2c_write(state, REG_CMDRD_PAGE, PAGE_IF_SW); +	s5k5baf_i2c_write(state, REG_CMDWR_PAGE, PAGE_IF_SW); +} + +/* + * V4L2 subdev core and video operations + */ + +static void s5k5baf_initialize_data(struct s5k5baf *state) +{ +	state->pixfmt = 0; +	state->req_fiv = 10000 / 15; +	state->fiv = state->req_fiv; +	state->valid_auto_alg = 0; +} + +static int s5k5baf_load_setfile(struct s5k5baf *state) +{ +	struct i2c_client *c = v4l2_get_subdevdata(&state->sd); +	const struct firmware *fw; +	int ret; + +	ret = request_firmware(&fw, S5K5BAF_FW_FILENAME, &c->dev); +	if (ret < 0) { +		dev_warn(&c->dev, "firmware file (%s) not loaded\n", +			 S5K5BAF_FW_FILENAME); +		return ret; +	} + +	ret = s5k5baf_fw_parse(&c->dev, &state->fw, fw->size / 2, +			       (u16 *)fw->data); + +	release_firmware(fw); + +	return ret; +} + +static int s5k5baf_set_power(struct v4l2_subdev *sd, int on) +{ +	struct s5k5baf *state = to_s5k5baf(sd); +	int ret = 0; + +	mutex_lock(&state->lock); + +	if (!on != state->power) +		goto out; + +	if (on) { +		if (state->fw == NULL) +			s5k5baf_load_setfile(state); + +		s5k5baf_initialize_data(state); +		ret = s5k5baf_power_on(state); +		if (ret < 0) +			goto out; + +		s5k5baf_hw_init(state); +		s5k5baf_hw_patch(state); +		s5k5baf_i2c_write(state, REG_SET_HOST_INT, 1); +		s5k5baf_hw_set_clocks(state); + +		ret = s5k5baf_hw_set_video_bus(state); +		if (ret < 0) +			goto out; + +		s5k5baf_hw_set_cis(state); +		s5k5baf_hw_set_ccm(state); + +		ret = s5k5baf_clear_error(state); +		if (!ret) +			state->power++; +	} else { +		s5k5baf_power_off(state); +		state->power--; +	} + +out: +	mutex_unlock(&state->lock); + +	if (!ret && on) +		ret = v4l2_ctrl_handler_setup(&state->ctrls.handler); + +	return ret; +} + +static void s5k5baf_hw_set_stream(struct s5k5baf *state, int enable) +{ +	s5k5baf_write_seq(state, REG_G_ENABLE_PREV, enable, 1); +} + +static int s5k5baf_s_stream(struct v4l2_subdev *sd, int on) +{ +	struct s5k5baf *state = to_s5k5baf(sd); +	int ret; + +	mutex_lock(&state->lock); + +	if (state->streaming == !!on) { +		ret = 0; +		goto out; +	} + +	if (on) { +		s5k5baf_hw_set_config(state); +		ret = s5k5baf_hw_set_crop_rects(state); +		if (ret < 0) +			goto out; +		s5k5baf_hw_set_stream(state, 1); +		s5k5baf_i2c_write(state, 0xb0cc, 0x000b); +	} else { +		s5k5baf_hw_set_stream(state, 0); +	} +	ret = s5k5baf_clear_error(state); +	if (!ret) +		state->streaming = !state->streaming; + +out: +	mutex_unlock(&state->lock); + +	return ret; +} + +static int s5k5baf_g_frame_interval(struct v4l2_subdev *sd, +				   struct v4l2_subdev_frame_interval *fi) +{ +	struct s5k5baf *state = to_s5k5baf(sd); + +	mutex_lock(&state->lock); +	fi->interval.numerator = state->fiv; +	fi->interval.denominator = 10000; +	mutex_unlock(&state->lock); + +	return 0; +} + +static void s5k5baf_set_frame_interval(struct s5k5baf *state, +				       struct v4l2_subdev_frame_interval *fi) +{ +	struct v4l2_fract *i = &fi->interval; + +	if (fi->interval.denominator == 0) +		state->req_fiv = S5K5BAF_MAX_FR_TIME; +	else +		state->req_fiv = clamp_t(u32, +					 i->numerator * 10000 / i->denominator, +					 S5K5BAF_MIN_FR_TIME, +					 S5K5BAF_MAX_FR_TIME); + +	state->fiv = state->req_fiv; +	if (state->apply_cfg) { +		s5k5baf_hw_set_fiv(state, state->req_fiv); +		s5k5baf_hw_validate_cfg(state); +	} +	*i = (struct v4l2_fract){ state->fiv, 10000 }; +	if (state->fiv == state->req_fiv) +		v4l2_info(&state->sd, "frame interval changed to %d00us\n", +			  state->fiv); +} + +static int s5k5baf_s_frame_interval(struct v4l2_subdev *sd, +				   struct v4l2_subdev_frame_interval *fi) +{ +	struct s5k5baf *state = to_s5k5baf(sd); + +	mutex_lock(&state->lock); +	s5k5baf_set_frame_interval(state, fi); +	mutex_unlock(&state->lock); +	return 0; +} + +/* + * V4L2 subdev pad level and video operations + */ +static int s5k5baf_enum_frame_interval(struct v4l2_subdev *sd, +			      struct v4l2_subdev_fh *fh, +			      struct v4l2_subdev_frame_interval_enum *fie) +{ +	if (fie->index > S5K5BAF_MAX_FR_TIME - S5K5BAF_MIN_FR_TIME || +	    fie->pad != PAD_CIS) +		return -EINVAL; + +	v4l_bound_align_image(&fie->width, S5K5BAF_WIN_WIDTH_MIN, +			      S5K5BAF_CIS_WIDTH, 1, +			      &fie->height, S5K5BAF_WIN_HEIGHT_MIN, +			      S5K5BAF_CIS_HEIGHT, 1, 0); + +	fie->interval.numerator = S5K5BAF_MIN_FR_TIME + fie->index; +	fie->interval.denominator = 10000; + +	return 0; +} + +static int s5k5baf_enum_mbus_code(struct v4l2_subdev *sd, +				 struct v4l2_subdev_fh *fh, +				 struct v4l2_subdev_mbus_code_enum *code) +{ +	if (code->pad == PAD_CIS) { +		if (code->index > 0) +			return -EINVAL; +		code->code = V4L2_MBUS_FMT_FIXED; +		return 0; +	} + +	if (code->index >= ARRAY_SIZE(s5k5baf_formats)) +		return -EINVAL; + +	code->code = s5k5baf_formats[code->index].code; +	return 0; +} + +static int s5k5baf_enum_frame_size(struct v4l2_subdev *sd, +				  struct v4l2_subdev_fh *fh, +				  struct v4l2_subdev_frame_size_enum *fse) +{ +	int i; + +	if (fse->index > 0) +		return -EINVAL; + +	if (fse->pad == PAD_CIS) { +		fse->code = V4L2_MBUS_FMT_FIXED; +		fse->min_width = S5K5BAF_CIS_WIDTH; +		fse->max_width = S5K5BAF_CIS_WIDTH; +		fse->min_height = S5K5BAF_CIS_HEIGHT; +		fse->max_height = S5K5BAF_CIS_HEIGHT; +		return 0; +	} + +	i = ARRAY_SIZE(s5k5baf_formats); +	while (--i) +		if (fse->code == s5k5baf_formats[i].code) +			break; +	fse->code = s5k5baf_formats[i].code; +	fse->min_width = S5K5BAF_WIN_WIDTH_MIN; +	fse->max_width = S5K5BAF_CIS_WIDTH; +	fse->max_height = S5K5BAF_WIN_HEIGHT_MIN; +	fse->min_height = S5K5BAF_CIS_HEIGHT; + +	return 0; +} + +static void s5k5baf_try_cis_format(struct v4l2_mbus_framefmt *mf) +{ +	mf->width = S5K5BAF_CIS_WIDTH; +	mf->height = S5K5BAF_CIS_HEIGHT; +	mf->code = V4L2_MBUS_FMT_FIXED; +	mf->colorspace = V4L2_COLORSPACE_JPEG; +	mf->field = V4L2_FIELD_NONE; +} + +static int s5k5baf_try_isp_format(struct v4l2_mbus_framefmt *mf) +{ +	int pixfmt; + +	v4l_bound_align_image(&mf->width, S5K5BAF_WIN_WIDTH_MIN, +			      S5K5BAF_CIS_WIDTH, 1, +			      &mf->height, S5K5BAF_WIN_HEIGHT_MIN, +			      S5K5BAF_CIS_HEIGHT, 1, 0); + +	pixfmt = s5k5baf_find_pixfmt(mf); + +	mf->colorspace = s5k5baf_formats[pixfmt].colorspace; +	mf->code = s5k5baf_formats[pixfmt].code; +	mf->field = V4L2_FIELD_NONE; + +	return pixfmt; +} + +static int s5k5baf_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +			  struct v4l2_subdev_format *fmt) +{ +	struct s5k5baf *state = to_s5k5baf(sd); +	const struct s5k5baf_pixfmt *pixfmt; +	struct v4l2_mbus_framefmt *mf; + +	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +		mf = v4l2_subdev_get_try_format(fh, fmt->pad); +		fmt->format = *mf; +		return 0; +	} + +	mf = &fmt->format; +	if (fmt->pad == PAD_CIS) { +		s5k5baf_try_cis_format(mf); +		return 0; +	} +	mf->field = V4L2_FIELD_NONE; +	mutex_lock(&state->lock); +	pixfmt = &s5k5baf_formats[state->pixfmt]; +	mf->width = state->crop_source.width; +	mf->height = state->crop_source.height; +	mf->code = pixfmt->code; +	mf->colorspace = pixfmt->colorspace; +	mutex_unlock(&state->lock); + +	return 0; +} + +static int s5k5baf_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +			  struct v4l2_subdev_format *fmt) +{ +	struct v4l2_mbus_framefmt *mf = &fmt->format; +	struct s5k5baf *state = to_s5k5baf(sd); +	const struct s5k5baf_pixfmt *pixfmt; +	int ret = 0; + +	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +		*v4l2_subdev_get_try_format(fh, fmt->pad) = *mf; +		return 0; +	} + +	if (fmt->pad == PAD_CIS) { +		s5k5baf_try_cis_format(mf); +		return 0; +	} + +	mutex_lock(&state->lock); + +	if (state->streaming) { +		mutex_unlock(&state->lock); +		return -EBUSY; +	} + +	state->pixfmt = s5k5baf_try_isp_format(mf); +	pixfmt = &s5k5baf_formats[state->pixfmt]; +	mf->code = pixfmt->code; +	mf->colorspace = pixfmt->colorspace; +	mf->width = state->crop_source.width; +	mf->height = state->crop_source.height; + +	mutex_unlock(&state->lock); +	return ret; +} + +enum selection_rect { R_CIS, R_CROP_SINK, R_COMPOSE, R_CROP_SOURCE, R_INVALID }; + +static enum selection_rect s5k5baf_get_sel_rect(u32 pad, u32 target) +{ +	switch (target) { +	case V4L2_SEL_TGT_CROP_BOUNDS: +		return pad ? R_COMPOSE : R_CIS; +	case V4L2_SEL_TGT_CROP: +		return pad ? R_CROP_SOURCE : R_CROP_SINK; +	case V4L2_SEL_TGT_COMPOSE_BOUNDS: +		return pad ? R_INVALID : R_CROP_SINK; +	case V4L2_SEL_TGT_COMPOSE: +		return pad ? R_INVALID : R_COMPOSE; +	default: +		return R_INVALID; +	} +} + +static int s5k5baf_is_bound_target(u32 target) +{ +	return target == V4L2_SEL_TGT_CROP_BOUNDS || +		target == V4L2_SEL_TGT_COMPOSE_BOUNDS; +} + +static int s5k5baf_get_selection(struct v4l2_subdev *sd, +				 struct v4l2_subdev_fh *fh, +				 struct v4l2_subdev_selection *sel) +{ +	static enum selection_rect rtype; +	struct s5k5baf *state = to_s5k5baf(sd); + +	rtype = s5k5baf_get_sel_rect(sel->pad, sel->target); + +	switch (rtype) { +	case R_INVALID: +		return -EINVAL; +	case R_CIS: +		sel->r = s5k5baf_cis_rect; +		return 0; +	default: +		break; +	} + +	if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { +		if (rtype == R_COMPOSE) +			sel->r = *v4l2_subdev_get_try_compose(fh, sel->pad); +		else +			sel->r = *v4l2_subdev_get_try_crop(fh, sel->pad); +		return 0; +	} + +	mutex_lock(&state->lock); +	switch (rtype) { +	case R_CROP_SINK: +		sel->r = state->crop_sink; +		break; +	case R_COMPOSE: +		sel->r = state->compose; +		break; +	case R_CROP_SOURCE: +		sel->r = state->crop_source; +		break; +	default: +		break; +	} +	if (s5k5baf_is_bound_target(sel->target)) { +		sel->r.left = 0; +		sel->r.top = 0; +	} +	mutex_unlock(&state->lock); + +	return 0; +} + +/* bounds range [start, start+len) to [0, max) and aligns to 2 */ +static void s5k5baf_bound_range(u32 *start, u32 *len, u32 max) +{ +	if (*len > max) +		*len = max; +	if (*start + *len > max) +		*start = max - *len; +	*start &= ~1; +	*len &= ~1; +	if (*len < S5K5BAF_WIN_WIDTH_MIN) +		*len = S5K5BAF_WIN_WIDTH_MIN; +} + +static void s5k5baf_bound_rect(struct v4l2_rect *r, u32 width, u32 height) +{ +	s5k5baf_bound_range(&r->left, &r->width, width); +	s5k5baf_bound_range(&r->top, &r->height, height); +} + +static void s5k5baf_set_rect_and_adjust(struct v4l2_rect **rects, +					enum selection_rect first, +					struct v4l2_rect *v) +{ +	struct v4l2_rect *r, *br; +	enum selection_rect i = first; + +	*rects[first] = *v; +	do { +		r = rects[i]; +		br = rects[i - 1]; +		s5k5baf_bound_rect(r, br->width, br->height); +	} while (++i != R_INVALID); +	*v = *rects[first]; +} + +static bool s5k5baf_cmp_rect(const struct v4l2_rect *r1, +			     const struct v4l2_rect *r2) +{ +	return !memcmp(r1, r2, sizeof(*r1)); +} + +static int s5k5baf_set_selection(struct v4l2_subdev *sd, +				 struct v4l2_subdev_fh *fh, +				 struct v4l2_subdev_selection *sel) +{ +	static enum selection_rect rtype; +	struct s5k5baf *state = to_s5k5baf(sd); +	struct v4l2_rect **rects; +	int ret = 0; + +	rtype = s5k5baf_get_sel_rect(sel->pad, sel->target); +	if (rtype == R_INVALID || s5k5baf_is_bound_target(sel->target)) +		return -EINVAL; + +	/* allow only scaling on compose */ +	if (rtype == R_COMPOSE) { +		sel->r.left = 0; +		sel->r.top = 0; +	} + +	if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { +		rects = (struct v4l2_rect * []) { +				&s5k5baf_cis_rect, +				v4l2_subdev_get_try_crop(fh, PAD_CIS), +				v4l2_subdev_get_try_compose(fh, PAD_CIS), +				v4l2_subdev_get_try_crop(fh, PAD_OUT) +			}; +		s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r); +		return 0; +	} + +	rects = (struct v4l2_rect * []) { +			&s5k5baf_cis_rect, +			&state->crop_sink, +			&state->compose, +			&state->crop_source +		}; +	mutex_lock(&state->lock); +	if (state->streaming) { +		/* adjust sel->r to avoid output resolution change */ +		if (rtype < R_CROP_SOURCE) { +			if (sel->r.width < state->crop_source.width) +				sel->r.width = state->crop_source.width; +			if (sel->r.height < state->crop_source.height) +				sel->r.height = state->crop_source.height; +		} else { +			sel->r.width = state->crop_source.width; +			sel->r.height = state->crop_source.height; +		} +	} +	s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r); +	if (!s5k5baf_cmp_rect(&state->crop_sink, &s5k5baf_cis_rect) || +	    !s5k5baf_cmp_rect(&state->compose, &s5k5baf_cis_rect)) +		state->apply_crop = 1; +	if (state->streaming) +		ret = s5k5baf_hw_set_crop_rects(state); +	mutex_unlock(&state->lock); + +	return ret; +} + +static const struct v4l2_subdev_pad_ops s5k5baf_cis_pad_ops = { +	.enum_mbus_code		= s5k5baf_enum_mbus_code, +	.enum_frame_size	= s5k5baf_enum_frame_size, +	.get_fmt		= s5k5baf_get_fmt, +	.set_fmt		= s5k5baf_set_fmt, +}; + +static const struct v4l2_subdev_pad_ops s5k5baf_pad_ops = { +	.enum_mbus_code		= s5k5baf_enum_mbus_code, +	.enum_frame_size	= s5k5baf_enum_frame_size, +	.enum_frame_interval	= s5k5baf_enum_frame_interval, +	.get_fmt		= s5k5baf_get_fmt, +	.set_fmt		= s5k5baf_set_fmt, +	.get_selection		= s5k5baf_get_selection, +	.set_selection		= s5k5baf_set_selection, +}; + +static const struct v4l2_subdev_video_ops s5k5baf_video_ops = { +	.g_frame_interval	= s5k5baf_g_frame_interval, +	.s_frame_interval	= s5k5baf_s_frame_interval, +	.s_stream		= s5k5baf_s_stream, +}; + +/* + * V4L2 subdev controls + */ + +static int s5k5baf_s_ctrl(struct v4l2_ctrl *ctrl) +{ +	struct v4l2_subdev *sd = ctrl_to_sd(ctrl); +	struct s5k5baf *state = to_s5k5baf(sd); +	int ret; + +	v4l2_dbg(1, debug, sd, "ctrl: %s, value: %d\n", ctrl->name, ctrl->val); + +	mutex_lock(&state->lock); + +	if (state->power == 0) +		goto unlock; + +	switch (ctrl->id) { +	case V4L2_CID_AUTO_WHITE_BALANCE: +		s5k5baf_hw_set_awb(state, ctrl->val); +		break; + +	case V4L2_CID_BRIGHTNESS: +		s5k5baf_write(state, REG_USER_BRIGHTNESS, ctrl->val); +		break; + +	case V4L2_CID_COLORFX: +		s5k5baf_hw_set_colorfx(state, ctrl->val); +		break; + +	case V4L2_CID_CONTRAST: +		s5k5baf_write(state, REG_USER_CONTRAST, ctrl->val); +		break; + +	case V4L2_CID_EXPOSURE_AUTO: +		s5k5baf_hw_set_auto_exposure(state, ctrl->val); +		break; + +	case V4L2_CID_HFLIP: +		s5k5baf_hw_set_mirror(state); +		break; + +	case V4L2_CID_POWER_LINE_FREQUENCY: +		s5k5baf_hw_set_anti_flicker(state, ctrl->val); +		break; + +	case V4L2_CID_SATURATION: +		s5k5baf_write(state, REG_USER_SATURATION, ctrl->val); +		break; + +	case V4L2_CID_SHARPNESS: +		s5k5baf_write(state, REG_USER_SHARPBLUR, ctrl->val); +		break; + +	case V4L2_CID_WHITE_BALANCE_TEMPERATURE: +		s5k5baf_write(state, REG_P_COLORTEMP(0), ctrl->val); +		if (state->apply_cfg) +			s5k5baf_hw_sync_cfg(state); +		break; + +	case V4L2_CID_TEST_PATTERN: +		s5k5baf_hw_set_test_pattern(state, ctrl->val); +		break; +	} +unlock: +	ret = s5k5baf_clear_error(state); +	mutex_unlock(&state->lock); +	return ret; +} + +static const struct v4l2_ctrl_ops s5k5baf_ctrl_ops = { +	.s_ctrl	= s5k5baf_s_ctrl, +}; + +static const char * const s5k5baf_test_pattern_menu[] = { +	"Disabled", +	"Blank", +	"Bars", +	"Gradients", +	"Textile", +	"Textile2", +	"Squares" +}; + +static int s5k5baf_initialize_ctrls(struct s5k5baf *state) +{ +	const struct v4l2_ctrl_ops *ops = &s5k5baf_ctrl_ops; +	struct s5k5baf_ctrls *ctrls = &state->ctrls; +	struct v4l2_ctrl_handler *hdl = &ctrls->handler; +	int ret; + +	ret = v4l2_ctrl_handler_init(hdl, 16); +	if (ret < 0) { +		v4l2_err(&state->sd, "cannot init ctrl handler (%d)\n", ret); +		return ret; +	} + +	/* Auto white balance cluster */ +	ctrls->awb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, +				       0, 1, 1, 1); +	ctrls->gain_red = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE, +					    0, 255, 1, S5K5BAF_GAIN_RED_DEF); +	ctrls->gain_blue = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE, +					     0, 255, 1, S5K5BAF_GAIN_BLUE_DEF); +	v4l2_ctrl_auto_cluster(3, &ctrls->awb, 0, false); + +	ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); +	ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); +	v4l2_ctrl_cluster(2, &ctrls->hflip); + +	ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, +				V4L2_CID_EXPOSURE_AUTO, +				V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); +	/* Exposure time: x 1 us */ +	ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, +					    0, 6000000U, 1, 100000U); +	/* Total gain: 256 <=> 1x */ +	ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, +					0, 256, 1, 256); +	v4l2_ctrl_auto_cluster(3, &ctrls->auto_exp, 0, false); + +	v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY, +			       V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, +			       V4L2_CID_POWER_LINE_FREQUENCY_AUTO); + +	v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX, +			       V4L2_COLORFX_SKY_BLUE, ~0x6f, V4L2_COLORFX_NONE); + +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_WHITE_BALANCE_TEMPERATURE, +			  0, 256, 1, 0); + +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, -127, 127, 1, 0); +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -127, 127, 1, 0); +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -127, 127, 1, 0); +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, -127, 127, 1, 0); + +	v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN, +				     ARRAY_SIZE(s5k5baf_test_pattern_menu) - 1, +				     0, 0, s5k5baf_test_pattern_menu); + +	if (hdl->error) { +		v4l2_err(&state->sd, "error creating controls (%d)\n", +			 hdl->error); +		ret = hdl->error; +		v4l2_ctrl_handler_free(hdl); +		return ret; +	} + +	state->sd.ctrl_handler = hdl; +	return 0; +} + +/* + * V4L2 subdev internal operations + */ +static int s5k5baf_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ +	struct v4l2_mbus_framefmt *mf; + +	mf = v4l2_subdev_get_try_format(fh, PAD_CIS); +	s5k5baf_try_cis_format(mf); + +	if (s5k5baf_is_cis_subdev(sd)) +		return 0; + +	mf = v4l2_subdev_get_try_format(fh, PAD_OUT); +	mf->colorspace = s5k5baf_formats[0].colorspace; +	mf->code = s5k5baf_formats[0].code; +	mf->width = s5k5baf_cis_rect.width; +	mf->height = s5k5baf_cis_rect.height; +	mf->field = V4L2_FIELD_NONE; + +	*v4l2_subdev_get_try_crop(fh, PAD_CIS) = s5k5baf_cis_rect; +	*v4l2_subdev_get_try_compose(fh, PAD_CIS) = s5k5baf_cis_rect; +	*v4l2_subdev_get_try_crop(fh, PAD_OUT) = s5k5baf_cis_rect; + +	return 0; +} + +static int s5k5baf_check_fw_revision(struct s5k5baf *state) +{ +	u16 api_ver = 0, fw_rev = 0, s_id = 0; +	int ret; + +	api_ver = s5k5baf_read(state, REG_FW_APIVER); +	fw_rev = s5k5baf_read(state, REG_FW_REVISION) & 0xff; +	s_id = s5k5baf_read(state, REG_FW_SENSOR_ID); +	ret = s5k5baf_clear_error(state); +	if (ret < 0) +		return ret; + +	v4l2_info(&state->sd, "FW API=%#x, revision=%#x sensor_id=%#x\n", +		  api_ver, fw_rev, s_id); + +	if (api_ver != S5K5BAF_FW_APIVER) { +		v4l2_err(&state->sd, "FW API version not supported\n"); +		return -ENODEV; +	} + +	return 0; +} + +static int s5k5baf_registered(struct v4l2_subdev *sd) +{ +	struct s5k5baf *state = to_s5k5baf(sd); +	int ret; + +	ret = v4l2_device_register_subdev(sd->v4l2_dev, &state->cis_sd); +	if (ret < 0) +		v4l2_err(sd, "failed to register subdev %s\n", +			 state->cis_sd.name); +	else +		ret = media_entity_create_link(&state->cis_sd.entity, PAD_CIS, +					       &state->sd.entity, PAD_CIS, +					       MEDIA_LNK_FL_IMMUTABLE | +					       MEDIA_LNK_FL_ENABLED); +	return ret; +} + +static void s5k5baf_unregistered(struct v4l2_subdev *sd) +{ +	struct s5k5baf *state = to_s5k5baf(sd); +	v4l2_device_unregister_subdev(&state->cis_sd); +} + +static const struct v4l2_subdev_ops s5k5baf_cis_subdev_ops = { +	.pad	= &s5k5baf_cis_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops s5k5baf_cis_subdev_internal_ops = { +	.open = s5k5baf_open, +}; + +static const struct v4l2_subdev_internal_ops s5k5baf_subdev_internal_ops = { +	.registered = s5k5baf_registered, +	.unregistered = s5k5baf_unregistered, +	.open = s5k5baf_open, +}; + +static const struct v4l2_subdev_core_ops s5k5baf_core_ops = { +	.s_power = s5k5baf_set_power, +	.log_status = v4l2_ctrl_subdev_log_status, +}; + +static const struct v4l2_subdev_ops s5k5baf_subdev_ops = { +	.core = &s5k5baf_core_ops, +	.pad = &s5k5baf_pad_ops, +	.video = &s5k5baf_video_ops, +}; + +static int s5k5baf_configure_gpios(struct s5k5baf *state) +{ +	static const char const *name[] = { "S5K5BAF_STBY", "S5K5BAF_RST" }; +	struct i2c_client *c = v4l2_get_subdevdata(&state->sd); +	struct s5k5baf_gpio *g = state->gpios; +	int ret, i; + +	for (i = 0; i < NUM_GPIOS; ++i) { +		int flags = GPIOF_DIR_OUT; +		if (g[i].level) +			flags |= GPIOF_INIT_HIGH; +		ret = devm_gpio_request_one(&c->dev, g[i].gpio, flags, name[i]); +		if (ret < 0) { +			v4l2_err(c, "failed to request gpio %s\n", name[i]); +			return ret; +		} +	} +	return 0; +} + +static int s5k5baf_parse_gpios(struct s5k5baf_gpio *gpios, struct device *dev) +{ +	static const char * const names[] = { +		"stbyn-gpios", +		"rstn-gpios", +	}; +	struct device_node *node = dev->of_node; +	enum of_gpio_flags flags; +	int ret, i; + +	for (i = 0; i < NUM_GPIOS; ++i) { +		ret = of_get_named_gpio_flags(node, names[i], 0, &flags); +		if (ret < 0) { +			dev_err(dev, "no %s GPIO pin provided\n", names[i]); +			return ret; +		} +		gpios[i].gpio = ret; +		gpios[i].level = !(flags & OF_GPIO_ACTIVE_LOW); +	} + +	return 0; +} + +static int s5k5baf_parse_device_node(struct s5k5baf *state, struct device *dev) +{ +	struct device_node *node = dev->of_node; +	struct device_node *node_ep; +	struct v4l2_of_endpoint ep; +	int ret; + +	if (!node) { +		dev_err(dev, "no device-tree node provided\n"); +		return -EINVAL; +	} + +	ret = of_property_read_u32(node, "clock-frequency", +				   &state->mclk_frequency); +	if (ret < 0) { +		state->mclk_frequency = S5K5BAF_DEFAULT_MCLK_FREQ; +		dev_info(dev, "using default %u Hz clock frequency\n", +			 state->mclk_frequency); +	} + +	ret = s5k5baf_parse_gpios(state->gpios, dev); +	if (ret < 0) +		return ret; + +	node_ep = of_graph_get_next_endpoint(node, NULL); +	if (!node_ep) { +		dev_err(dev, "no endpoint defined at node %s\n", +			node->full_name); +		return -EINVAL; +	} + +	v4l2_of_parse_endpoint(node_ep, &ep); +	of_node_put(node_ep); +	state->bus_type = ep.bus_type; + +	switch (state->bus_type) { +	case V4L2_MBUS_CSI2: +		state->nlanes = ep.bus.mipi_csi2.num_data_lanes; +		break; +	case V4L2_MBUS_PARALLEL: +		break; +	default: +		dev_err(dev, "unsupported bus in endpoint defined at node %s\n", +			node->full_name); +		return -EINVAL; +	} + +	return 0; +} + +static int s5k5baf_configure_subdevs(struct s5k5baf *state, +				     struct i2c_client *c) +{ +	struct v4l2_subdev *sd; +	int ret; + +	sd = &state->cis_sd; +	v4l2_subdev_init(sd, &s5k5baf_cis_subdev_ops); +	sd->owner = THIS_MODULE; +	v4l2_set_subdevdata(sd, state); +	snprintf(sd->name, sizeof(sd->name), "S5K5BAF-CIS %d-%04x", +		 i2c_adapter_id(c->adapter), c->addr); + +	sd->internal_ops = &s5k5baf_cis_subdev_internal_ops; +	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + +	state->cis_pad.flags = MEDIA_PAD_FL_SOURCE; +	sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; +	ret = media_entity_init(&sd->entity, NUM_CIS_PADS, &state->cis_pad, 0); +	if (ret < 0) +		goto err; + +	sd = &state->sd; +	v4l2_i2c_subdev_init(sd, c, &s5k5baf_subdev_ops); +	snprintf(sd->name, sizeof(sd->name), "S5K5BAF-ISP %d-%04x", +		 i2c_adapter_id(c->adapter), c->addr); + +	sd->internal_ops = &s5k5baf_subdev_internal_ops; +	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + +	state->pads[PAD_CIS].flags = MEDIA_PAD_FL_SINK; +	state->pads[PAD_OUT].flags = MEDIA_PAD_FL_SOURCE; +	sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV; +	ret = media_entity_init(&sd->entity, NUM_ISP_PADS, state->pads, 0); + +	if (!ret) +		return 0; + +	media_entity_cleanup(&state->cis_sd.entity); +err: +	dev_err(&c->dev, "cannot init media entity %s\n", sd->name); +	return ret; +} + +static int s5k5baf_configure_regulators(struct s5k5baf *state) +{ +	struct i2c_client *c = v4l2_get_subdevdata(&state->sd); +	int ret; +	int i; + +	for (i = 0; i < S5K5BAF_NUM_SUPPLIES; i++) +		state->supplies[i].supply = s5k5baf_supply_names[i]; + +	ret = devm_regulator_bulk_get(&c->dev, S5K5BAF_NUM_SUPPLIES, +				      state->supplies); +	if (ret < 0) +		v4l2_err(c, "failed to get regulators\n"); +	return ret; +} + +static int s5k5baf_probe(struct i2c_client *c, +			const struct i2c_device_id *id) +{ +	struct s5k5baf *state; +	int ret; + +	state = devm_kzalloc(&c->dev, sizeof(*state), GFP_KERNEL); +	if (!state) +		return -ENOMEM; + +	mutex_init(&state->lock); +	state->crop_sink = s5k5baf_cis_rect; +	state->compose = s5k5baf_cis_rect; +	state->crop_source = s5k5baf_cis_rect; + +	ret = s5k5baf_parse_device_node(state, &c->dev); +	if (ret < 0) +		return ret; + +	ret = s5k5baf_configure_subdevs(state, c); +	if (ret < 0) +		return ret; + +	ret = s5k5baf_configure_gpios(state); +	if (ret < 0) +		goto err_me; + +	ret = s5k5baf_configure_regulators(state); +	if (ret < 0) +		goto err_me; + +	state->clock = devm_clk_get(state->sd.dev, S5K5BAF_CLK_NAME); +	if (IS_ERR(state->clock)) { +		ret = -EPROBE_DEFER; +		goto err_me; +	} + +	ret = s5k5baf_power_on(state); +	if (ret < 0) { +		ret = -EPROBE_DEFER; +		goto err_me; +	} +	s5k5baf_hw_init(state); +	ret = s5k5baf_check_fw_revision(state); + +	s5k5baf_power_off(state); +	if (ret < 0) +		goto err_me; + +	ret = s5k5baf_initialize_ctrls(state); +	if (ret < 0) +		goto err_me; + +	ret = v4l2_async_register_subdev(&state->sd); +	if (ret < 0) +		goto err_ctrl; + +	return 0; + +err_ctrl: +	v4l2_ctrl_handler_free(state->sd.ctrl_handler); +err_me: +	media_entity_cleanup(&state->sd.entity); +	media_entity_cleanup(&state->cis_sd.entity); +	return ret; +} + +static int s5k5baf_remove(struct i2c_client *c) +{ +	struct v4l2_subdev *sd = i2c_get_clientdata(c); +	struct s5k5baf *state = to_s5k5baf(sd); + +	v4l2_async_unregister_subdev(sd); +	v4l2_ctrl_handler_free(sd->ctrl_handler); +	media_entity_cleanup(&sd->entity); + +	sd = &state->cis_sd; +	v4l2_device_unregister_subdev(sd); +	media_entity_cleanup(&sd->entity); + +	return 0; +} + +static const struct i2c_device_id s5k5baf_id[] = { +	{ S5K5BAF_DRIVER_NAME, 0 }, +	{ }, +}; +MODULE_DEVICE_TABLE(i2c, s5k5baf_id); + +static const struct of_device_id s5k5baf_of_match[] = { +	{ .compatible = "samsung,s5k5baf" }, +	{ } +}; +MODULE_DEVICE_TABLE(of, s5k5baf_of_match); + +static struct i2c_driver s5k5baf_i2c_driver = { +	.driver = { +		.of_match_table = s5k5baf_of_match, +		.name = S5K5BAF_DRIVER_NAME +	}, +	.probe		= s5k5baf_probe, +	.remove		= s5k5baf_remove, +	.id_table	= s5k5baf_id, +}; + +module_i2c_driver(s5k5baf_i2c_driver); + +MODULE_DESCRIPTION("Samsung S5K5BAF(X) UXGA camera driver"); +MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c new file mode 100644 index 00000000000..7bc2271ca00 --- /dev/null +++ b/drivers/media/i2c/s5k6a3.c @@ -0,0 +1,389 @@ +/* + * Samsung S5K6A3 image sensor driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_gpio.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/videodev2.h> +#include <media/v4l2-async.h> +#include <media/v4l2-subdev.h> + +#define S5K6A3_SENSOR_MAX_WIDTH		1412 +#define S5K6A3_SENSOR_MAX_HEIGHT	1412 +#define S5K6A3_SENSOR_MIN_WIDTH		32 +#define S5K6A3_SENSOR_MIN_HEIGHT	32 + +#define S5K6A3_DEFAULT_WIDTH		1296 +#define S5K6A3_DEFAULT_HEIGHT		732 + +#define S5K6A3_DRV_NAME			"S5K6A3" +#define S5K6A3_CLK_NAME			"extclk" +#define S5K6A3_DEFAULT_CLK_FREQ		24000000U + +enum { +	S5K6A3_SUPP_VDDA, +	S5K6A3_SUPP_VDDIO, +	S5K6A3_SUPP_AFVDD, +	S5K6A3_NUM_SUPPLIES, +}; + +/** + * struct s5k6a3 - fimc-is sensor data structure + * @dev: pointer to this I2C client device structure + * @subdev: the image sensor's v4l2 subdev + * @pad: subdev media source pad + * @supplies: image sensor's voltage regulator supplies + * @gpio_reset: GPIO connected to the sensor's reset pin + * @lock: mutex protecting the structure's members below + * @format: media bus format at the sensor's source pad + */ +struct s5k6a3 { +	struct device *dev; +	struct v4l2_subdev subdev; +	struct media_pad pad; +	struct regulator_bulk_data supplies[S5K6A3_NUM_SUPPLIES]; +	int gpio_reset; +	struct mutex lock; +	struct v4l2_mbus_framefmt format; +	struct clk *clock; +	u32 clock_frequency; +	int power_count; +}; + +static const char * const s5k6a3_supply_names[] = { +	[S5K6A3_SUPP_VDDA]	= "svdda", +	[S5K6A3_SUPP_VDDIO]	= "svddio", +	[S5K6A3_SUPP_AFVDD]	= "afvdd", +}; + +static inline struct s5k6a3 *sd_to_s5k6a3(struct v4l2_subdev *sd) +{ +	return container_of(sd, struct s5k6a3, subdev); +} + +static const struct v4l2_mbus_framefmt s5k6a3_formats[] = { +	{ +		.code = V4L2_MBUS_FMT_SGRBG10_1X10, +		.colorspace = V4L2_COLORSPACE_SRGB, +		.field = V4L2_FIELD_NONE, +	} +}; + +static const struct v4l2_mbus_framefmt *find_sensor_format( +	struct v4l2_mbus_framefmt *mf) +{ +	int i; + +	for (i = 0; i < ARRAY_SIZE(s5k6a3_formats); i++) +		if (mf->code == s5k6a3_formats[i].code) +			return &s5k6a3_formats[i]; + +	return &s5k6a3_formats[0]; +} + +static int s5k6a3_enum_mbus_code(struct v4l2_subdev *sd, +				  struct v4l2_subdev_fh *fh, +				  struct v4l2_subdev_mbus_code_enum *code) +{ +	if (code->index >= ARRAY_SIZE(s5k6a3_formats)) +		return -EINVAL; + +	code->code = s5k6a3_formats[code->index].code; +	return 0; +} + +static void s5k6a3_try_format(struct v4l2_mbus_framefmt *mf) +{ +	const struct v4l2_mbus_framefmt *fmt; + +	fmt = find_sensor_format(mf); +	mf->code = fmt->code; +	v4l_bound_align_image(&mf->width, S5K6A3_SENSOR_MIN_WIDTH, +			      S5K6A3_SENSOR_MAX_WIDTH, 0, +			      &mf->height, S5K6A3_SENSOR_MIN_HEIGHT, +			      S5K6A3_SENSOR_MAX_HEIGHT, 0, 0); +} + +static struct v4l2_mbus_framefmt *__s5k6a3_get_format( +		struct s5k6a3 *sensor, struct v4l2_subdev_fh *fh, +		u32 pad, enum v4l2_subdev_format_whence which) +{ +	if (which == V4L2_SUBDEV_FORMAT_TRY) +		return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL; + +	return &sensor->format; +} + +static int s5k6a3_set_fmt(struct v4l2_subdev *sd, +				  struct v4l2_subdev_fh *fh, +				  struct v4l2_subdev_format *fmt) +{ +	struct s5k6a3 *sensor = sd_to_s5k6a3(sd); +	struct v4l2_mbus_framefmt *mf; + +	s5k6a3_try_format(&fmt->format); + +	mf = __s5k6a3_get_format(sensor, fh, fmt->pad, fmt->which); +	if (mf) { +		mutex_lock(&sensor->lock); +		if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) +			*mf = fmt->format; +		mutex_unlock(&sensor->lock); +	} +	return 0; +} + +static int s5k6a3_get_fmt(struct v4l2_subdev *sd, +				  struct v4l2_subdev_fh *fh, +				  struct v4l2_subdev_format *fmt) +{ +	struct s5k6a3 *sensor = sd_to_s5k6a3(sd); +	struct v4l2_mbus_framefmt *mf; + +	mf = __s5k6a3_get_format(sensor, fh, fmt->pad, fmt->which); + +	mutex_lock(&sensor->lock); +	fmt->format = *mf; +	mutex_unlock(&sensor->lock); +	return 0; +} + +static struct v4l2_subdev_pad_ops s5k6a3_pad_ops = { +	.enum_mbus_code	= s5k6a3_enum_mbus_code, +	.get_fmt	= s5k6a3_get_fmt, +	.set_fmt	= s5k6a3_set_fmt, +}; + +static int s5k6a3_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ +	struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); + +	*format		= s5k6a3_formats[0]; +	format->width	= S5K6A3_DEFAULT_WIDTH; +	format->height	= S5K6A3_DEFAULT_HEIGHT; + +	return 0; +} + +static const struct v4l2_subdev_internal_ops s5k6a3_sd_internal_ops = { +	.open = s5k6a3_open, +}; + +static int __s5k6a3_power_on(struct s5k6a3 *sensor) +{ +	int i = S5K6A3_SUPP_VDDA; +	int ret; + +	ret = clk_set_rate(sensor->clock, sensor->clock_frequency); +	if (ret < 0) +		return ret; + +	ret = pm_runtime_get(sensor->dev); +	if (ret < 0) +		return ret; + +	ret = regulator_enable(sensor->supplies[i].consumer); +	if (ret < 0) +		goto error_rpm_put; + +	ret = clk_prepare_enable(sensor->clock); +	if (ret < 0) +		goto error_reg_dis; + +	for (i++; i < S5K6A3_NUM_SUPPLIES; i++) { +		ret = regulator_enable(sensor->supplies[i].consumer); +		if (ret < 0) +			goto error_reg_dis; +	} + +	gpio_set_value(sensor->gpio_reset, 1); +	usleep_range(600, 800); +	gpio_set_value(sensor->gpio_reset, 0); +	usleep_range(600, 800); +	gpio_set_value(sensor->gpio_reset, 1); + +	/* Delay needed for the sensor initialization */ +	msleep(20); +	return 0; + +error_reg_dis: +	for (--i; i >= 0; --i) +		regulator_disable(sensor->supplies[i].consumer); +error_rpm_put: +	pm_runtime_put(sensor->dev); +	return ret; +} + +static int __s5k6a3_power_off(struct s5k6a3 *sensor) +{ +	int i; + +	gpio_set_value(sensor->gpio_reset, 0); + +	for (i = S5K6A3_NUM_SUPPLIES - 1; i >= 0; i--) +		regulator_disable(sensor->supplies[i].consumer); + +	clk_disable_unprepare(sensor->clock); +	pm_runtime_put(sensor->dev); +	return 0; +} + +static int s5k6a3_s_power(struct v4l2_subdev *sd, int on) +{ +	struct s5k6a3 *sensor = sd_to_s5k6a3(sd); +	int ret = 0; + +	mutex_lock(&sensor->lock); + +	if (sensor->power_count == !on) { +		if (on) +			ret = __s5k6a3_power_on(sensor); +		else +			ret = __s5k6a3_power_off(sensor); + +		if (ret == 0) +			sensor->power_count += on ? 1 : -1; +	} + +	mutex_unlock(&sensor->lock); +	return ret; +} + +static struct v4l2_subdev_core_ops s5k6a3_core_ops = { +	.s_power = s5k6a3_s_power, +}; + +static struct v4l2_subdev_ops s5k6a3_subdev_ops = { +	.core = &s5k6a3_core_ops, +	.pad = &s5k6a3_pad_ops, +}; + +static int s5k6a3_probe(struct i2c_client *client, +				const struct i2c_device_id *id) +{ +	struct device *dev = &client->dev; +	struct s5k6a3 *sensor; +	struct v4l2_subdev *sd; +	int gpio, i, ret; + +	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); +	if (!sensor) +		return -ENOMEM; + +	mutex_init(&sensor->lock); +	sensor->gpio_reset = -EINVAL; +	sensor->clock = ERR_PTR(-EINVAL); +	sensor->dev = dev; + +	sensor->clock = devm_clk_get(sensor->dev, S5K6A3_CLK_NAME); +	if (IS_ERR(sensor->clock)) +		return PTR_ERR(sensor->clock); + +	gpio = of_get_gpio_flags(dev->of_node, 0, NULL); +	if (!gpio_is_valid(gpio)) +		return gpio; + +	ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_LOW, +						S5K6A3_DRV_NAME); +	if (ret < 0) +		return ret; + +	sensor->gpio_reset = gpio; + +	if (of_property_read_u32(dev->of_node, "clock-frequency", +				 &sensor->clock_frequency)) { +		sensor->clock_frequency = S5K6A3_DEFAULT_CLK_FREQ; +		dev_info(dev, "using default %u Hz clock frequency\n", +					sensor->clock_frequency); +	} + +	for (i = 0; i < S5K6A3_NUM_SUPPLIES; i++) +		sensor->supplies[i].supply = s5k6a3_supply_names[i]; + +	ret = devm_regulator_bulk_get(&client->dev, S5K6A3_NUM_SUPPLIES, +				      sensor->supplies); +	if (ret < 0) +		return ret; + +	sd = &sensor->subdev; +	v4l2_i2c_subdev_init(sd, client, &s5k6a3_subdev_ops); +	sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; +	sd->internal_ops = &s5k6a3_sd_internal_ops; + +	sensor->format.code = s5k6a3_formats[0].code; +	sensor->format.width = S5K6A3_DEFAULT_WIDTH; +	sensor->format.height = S5K6A3_DEFAULT_HEIGHT; + +	sensor->pad.flags = MEDIA_PAD_FL_SOURCE; +	ret = media_entity_init(&sd->entity, 1, &sensor->pad, 0); +	if (ret < 0) +		return ret; + +	pm_runtime_no_callbacks(dev); +	pm_runtime_enable(dev); + +	ret = v4l2_async_register_subdev(sd); + +	if (ret < 0) { +		pm_runtime_disable(&client->dev); +		media_entity_cleanup(&sd->entity); +	} + +	return ret; +} + +static int s5k6a3_remove(struct i2c_client *client) +{ +	struct v4l2_subdev *sd = i2c_get_clientdata(client); + +	pm_runtime_disable(&client->dev); +	v4l2_async_unregister_subdev(sd); +	media_entity_cleanup(&sd->entity); +	return 0; +} + +static const struct i2c_device_id s5k6a3_ids[] = { +	{ } +}; + +#ifdef CONFIG_OF +static const struct of_device_id s5k6a3_of_match[] = { +	{ .compatible = "samsung,s5k6a3" }, +	{ /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, s5k6a3_of_match); +#endif + +static struct i2c_driver s5k6a3_driver = { +	.driver = { +		.of_match_table	= of_match_ptr(s5k6a3_of_match), +		.name		= S5K6A3_DRV_NAME, +		.owner		= THIS_MODULE, +	}, +	.probe		= s5k6a3_probe, +	.remove		= s5k6a3_remove, +	.id_table	= s5k6a3_ids, +}; + +module_i2c_driver(s5k6a3_driver); + +MODULE_DESCRIPTION("S5K6A3 image sensor subdev driver"); +MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/saa6588.c b/drivers/media/i2c/saa6588.c index 70bc72e795d..2960b5a8362 100644 --- a/drivers/media/i2c/saa6588.c +++ b/drivers/media/i2c/saa6588.c @@ -150,14 +150,14 @@ static inline struct saa6588 *to_saa6588(struct v4l2_subdev *sd)  /* ---------------------------------------------------------------------- */ -static int block_to_user_buf(struct saa6588 *s, unsigned char __user *user_buf) +static bool block_from_buf(struct saa6588 *s, unsigned char *buf)  {  	int i;  	if (s->rd_index == s->wr_index) {  		if (debug > 2)  			dprintk(PREFIX "Read: buffer empty.\n"); -		return 0; +		return false;  	}  	if (debug > 2) { @@ -166,8 +166,7 @@ static int block_to_user_buf(struct saa6588 *s, unsigned char __user *user_buf)  			dprintk("0x%02x ", s->buffer[i]);  	} -	if (copy_to_user(user_buf, &s->buffer[s->rd_index], 3)) -		return -EFAULT; +	memcpy(buf, &s->buffer[s->rd_index], 3);  	s->rd_index += 3;  	if (s->rd_index >= s->buf_size) @@ -177,22 +176,22 @@ static int block_to_user_buf(struct saa6588 *s, unsigned char __user *user_buf)  	if (debug > 2)  		dprintk("%d blocks total.\n", s->block_count); -	return 1; +	return true;  }  static void read_from_buf(struct saa6588 *s, struct saa6588_command *a)  { -	unsigned long flags; -  	unsigned char __user *buf_ptr = a->buffer; -	unsigned int i; +	unsigned char buf[3]; +	unsigned long flags;  	unsigned int rd_blocks; +	unsigned int i;  	a->result = 0;  	if (!a->buffer)  		return; -	while (!s->data_available_for_read) { +	while (!a->nonblocking && !s->data_available_for_read) {  		int ret = wait_event_interruptible(s->read_queue,  					     s->data_available_for_read);  		if (ret == -ERESTARTSYS) { @@ -201,24 +200,31 @@ static void read_from_buf(struct saa6588 *s, struct saa6588_command *a)  		}  	} -	spin_lock_irqsave(&s->lock, flags);  	rd_blocks = a->block_count; +	spin_lock_irqsave(&s->lock, flags);  	if (rd_blocks > s->block_count)  		rd_blocks = s->block_count; +	spin_unlock_irqrestore(&s->lock, flags); -	if (!rd_blocks) { -		spin_unlock_irqrestore(&s->lock, flags); +	if (!rd_blocks)  		return; -	}  	for (i = 0; i < rd_blocks; i++) { -		if (block_to_user_buf(s, buf_ptr)) { -			buf_ptr += 3; -			a->result++; -		} else +		bool got_block; + +		spin_lock_irqsave(&s->lock, flags); +		got_block = block_from_buf(s, buf); +		spin_unlock_irqrestore(&s->lock, flags); +		if (!got_block)  			break; +		if (copy_to_user(buf_ptr, buf, 3)) { +			a->result = -EFAULT; +			return; +		} +		buf_ptr += 3; +		a->result += 3;  	} -	a->result *= 3; +	spin_lock_irqsave(&s->lock, flags);  	s->data_available_for_read = (s->block_count > 0);  	spin_unlock_irqrestore(&s->lock, flags);  } @@ -394,14 +400,11 @@ static long saa6588_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)  	struct saa6588_command *a = arg;  	switch (cmd) { -		/* --- open() for /dev/radio --- */ -	case SAA6588_CMD_OPEN: -		a->result = 0;	/* return error if chip doesn't work ??? */ -		break;  		/* --- close() for /dev/radio --- */  	case SAA6588_CMD_CLOSE:  		s->data_available_for_read = 1;  		wake_up_interruptible(&s->read_queue); +		s->data_available_for_read = 0;  		a->result = 0;  		break;  		/* --- read() for /dev/radio --- */ @@ -411,9 +414,8 @@ static long saa6588_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)  		/* --- poll() for /dev/radio --- */  	case SAA6588_CMD_POLL:  		a->result = 0; -		if (s->data_available_for_read) { +		if (s->data_available_for_read)  			a->result |= POLLIN | POLLRDNORM; -		}  		poll_wait(a->instance, &s->read_queue, a->event_list);  		break; diff --git a/drivers/media/i2c/saa6752hs.c b/drivers/media/i2c/saa6752hs.c new file mode 100644 index 00000000000..04e9e55018a --- /dev/null +++ b/drivers/media/i2c/saa6752hs.c @@ -0,0 +1,790 @@ + /* +    saa6752hs - i2c-driver for the saa6752hs by Philips + +    Copyright (C) 2004 Andrew de Quincey + +    AC-3 support: + +    Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> + +    This program is free software; you can redistribute it and/or modify +    it under the terms of the GNU General Public License vs published by +    the Free Software Foundation; either version 2 of the License, or +    (at your option) any later version. + +    This program is distributed in the hope that it will be useful, +    but WITHOUT ANY WARRANTY; without even the implied warranty of +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +    GNU General Public License for more details. + +    You should have received a copy of the GNU General Public License +    along with this program; if not, write to the Free Software +    Foundation, Inc., 675 Mvss Ave, Cambridge, MA 02139, USA. +  */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/poll.h> +#include <linux/i2c.h> +#include <linux/types.h> +#include <linux/videodev2.h> +#include <linux/init.h> +#include <linux/crc32.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-common.h> + +#define MPEG_VIDEO_TARGET_BITRATE_MAX  27000 +#define MPEG_VIDEO_MAX_BITRATE_MAX     27000 +#define MPEG_TOTAL_TARGET_BITRATE_MAX  27000 +#define MPEG_PID_MAX ((1 << 14) - 1) + + +MODULE_DESCRIPTION("device driver for saa6752hs MPEG2 encoder"); +MODULE_AUTHOR("Andrew de Quincey"); +MODULE_LICENSE("GPL"); + +enum saa6752hs_videoformat { +	SAA6752HS_VF_D1 = 0,    /* standard D1 video format: 720x576 */ +	SAA6752HS_VF_2_3_D1 = 1,/* 2/3D1 video format: 480x576 */ +	SAA6752HS_VF_1_2_D1 = 2,/* 1/2D1 video format: 352x576 */ +	SAA6752HS_VF_SIF = 3,   /* SIF video format: 352x288 */ +	SAA6752HS_VF_UNKNOWN, +}; + +struct saa6752hs_mpeg_params { +	/* transport streams */ +	__u16				ts_pid_pmt; +	__u16				ts_pid_audio; +	__u16				ts_pid_video; +	__u16				ts_pid_pcr; + +	/* audio */ +	enum v4l2_mpeg_audio_encoding    au_encoding; +	enum v4l2_mpeg_audio_l2_bitrate  au_l2_bitrate; +	enum v4l2_mpeg_audio_ac3_bitrate au_ac3_bitrate; + +	/* video */ +	enum v4l2_mpeg_video_aspect	vi_aspect; +	enum v4l2_mpeg_video_bitrate_mode vi_bitrate_mode; +	__u32 				vi_bitrate; +	__u32 				vi_bitrate_peak; +}; + +static const struct v4l2_format v4l2_format_table[] = +{ +	[SAA6752HS_VF_D1] = +		{ .fmt = { .pix = { .width = 720, .height = 576 }}}, +	[SAA6752HS_VF_2_3_D1] = +		{ .fmt = { .pix = { .width = 480, .height = 576 }}}, +	[SAA6752HS_VF_1_2_D1] = +		{ .fmt = { .pix = { .width = 352, .height = 576 }}}, +	[SAA6752HS_VF_SIF] = +		{ .fmt = { .pix = { .width = 352, .height = 288 }}}, +	[SAA6752HS_VF_UNKNOWN] = +		{ .fmt = { .pix = { .width = 0, .height = 0}}}, +}; + +struct saa6752hs_state { +	struct v4l2_subdev            sd; +	struct v4l2_ctrl_handler      hdl; +	struct { /* video bitrate mode control cluster */ +		struct v4l2_ctrl *video_bitrate_mode; +		struct v4l2_ctrl *video_bitrate; +		struct v4l2_ctrl *video_bitrate_peak; +	}; +	u32 			      revision; +	int 			      has_ac3; +	struct saa6752hs_mpeg_params  params; +	enum saa6752hs_videoformat    video_format; +	v4l2_std_id                   standard; +}; + +enum saa6752hs_command { +	SAA6752HS_COMMAND_RESET = 0, +	SAA6752HS_COMMAND_STOP = 1, +	SAA6752HS_COMMAND_START = 2, +	SAA6752HS_COMMAND_PAUSE = 3, +	SAA6752HS_COMMAND_RECONFIGURE = 4, +	SAA6752HS_COMMAND_SLEEP = 5, +	SAA6752HS_COMMAND_RECONFIGURE_FORCE = 6, + +	SAA6752HS_COMMAND_MAX +}; + +static inline struct saa6752hs_state *to_state(struct v4l2_subdev *sd) +{ +	return container_of(sd, struct saa6752hs_state, sd); +} + +/* ---------------------------------------------------------------------- */ + +static const u8 PAT[] = { +	0xc2, /* i2c register */ +	0x00, /* table number for encoder */ + +	0x47, /* sync */ +	0x40, 0x00, /* transport_error_indicator(0), payload_unit_start(1), transport_priority(0), pid(0) */ +	0x10, /* transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0) */ + +	0x00, /* PSI pointer to start of table */ + +	0x00, /* tid(0) */ +	0xb0, 0x0d, /* section_syntax_indicator(1), section_length(13) */ + +	0x00, 0x01, /* transport_stream_id(1) */ + +	0xc1, /* version_number(0), current_next_indicator(1) */ + +	0x00, 0x00, /* section_number(0), last_section_number(0) */ + +	0x00, 0x01, /* program_number(1) */ + +	0xe0, 0x00, /* PMT PID */ + +	0x00, 0x00, 0x00, 0x00 /* CRC32 */ +}; + +static const u8 PMT[] = { +	0xc2, /* i2c register */ +	0x01, /* table number for encoder */ + +	0x47, /* sync */ +	0x40, 0x00, /* transport_error_indicator(0), payload_unit_start(1), transport_priority(0), pid */ +	0x10, /* transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0) */ + +	0x00, /* PSI pointer to start of table */ + +	0x02, /* tid(2) */ +	0xb0, 0x17, /* section_syntax_indicator(1), section_length(23) */ + +	0x00, 0x01, /* program_number(1) */ + +	0xc1, /* version_number(0), current_next_indicator(1) */ + +	0x00, 0x00, /* section_number(0), last_section_number(0) */ + +	0xe0, 0x00, /* PCR_PID */ + +	0xf0, 0x00, /* program_info_length(0) */ + +	0x02, 0xe0, 0x00, 0xf0, 0x00, /* video stream type(2), pid */ +	0x04, 0xe0, 0x00, 0xf0, 0x00, /* audio stream type(4), pid */ + +	0x00, 0x00, 0x00, 0x00 /* CRC32 */ +}; + +static const u8 PMT_AC3[] = { +	0xc2, /* i2c register */ +	0x01, /* table number for encoder(1) */ +	0x47, /* sync */ + +	0x40, /* transport_error_indicator(0), payload_unit_start(1), transport_priority(0) */ +	0x10, /* PMT PID (0x0010) */ +	0x10, /* transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0) */ + +	0x00, /* PSI pointer to start of table */ + +	0x02, /* TID (2) */ +	0xb0, 0x1a, /* section_syntax_indicator(1), section_length(26) */ + +	0x00, 0x01, /* program_number(1) */ + +	0xc1, /* version_number(0), current_next_indicator(1) */ + +	0x00, 0x00, /* section_number(0), last_section_number(0) */ + +	0xe1, 0x04, /* PCR_PID (0x0104) */ + +	0xf0, 0x00, /* program_info_length(0) */ + +	0x02, 0xe1, 0x00, 0xf0, 0x00, /* video stream type(2), pid */ +	0x06, 0xe1, 0x03, 0xf0, 0x03, /* audio stream type(6), pid */ +	0x6a, /* AC3 */ +	0x01, /* Descriptor_length(1) */ +	0x00, /* component_type_flag(0), bsid_flag(0), mainid_flag(0), asvc_flag(0), reserved flags(0) */ + +	0xED, 0xDE, 0x2D, 0xF3 /* CRC32 BE */ +}; + +static const struct saa6752hs_mpeg_params param_defaults = +{ +	.ts_pid_pmt      = 16, +	.ts_pid_video    = 260, +	.ts_pid_audio    = 256, +	.ts_pid_pcr      = 259, + +	.vi_aspect       = V4L2_MPEG_VIDEO_ASPECT_4x3, +	.vi_bitrate      = 4000, +	.vi_bitrate_peak = 6000, +	.vi_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + +	.au_encoding     = V4L2_MPEG_AUDIO_ENCODING_LAYER_2, +	.au_l2_bitrate   = V4L2_MPEG_AUDIO_L2_BITRATE_256K, +	.au_ac3_bitrate  = V4L2_MPEG_AUDIO_AC3_BITRATE_256K, +}; + +/* ---------------------------------------------------------------------- */ + +static int saa6752hs_chip_command(struct i2c_client *client, +				  enum saa6752hs_command command) +{ +	unsigned char buf[3]; +	unsigned long timeout; +	int status = 0; + +	/* execute the command */ +	switch(command) { +	case SAA6752HS_COMMAND_RESET: +		buf[0] = 0x00; +		break; + +	case SAA6752HS_COMMAND_STOP: +		buf[0] = 0x03; +		break; + +	case SAA6752HS_COMMAND_START: +		buf[0] = 0x02; +		break; + +	case SAA6752HS_COMMAND_PAUSE: +		buf[0] = 0x04; +		break; + +	case SAA6752HS_COMMAND_RECONFIGURE: +		buf[0] = 0x05; +		break; + +	case SAA6752HS_COMMAND_SLEEP: +		buf[0] = 0x06; +		break; + +	case SAA6752HS_COMMAND_RECONFIGURE_FORCE: +		buf[0] = 0x07; +		break; + +	default: +		return -EINVAL; +	} + +	/* set it and wait for it to be so */ +	i2c_master_send(client, buf, 1); +	timeout = jiffies + HZ * 3; +	for (;;) { +		/* get the current status */ +		buf[0] = 0x10; +		i2c_master_send(client, buf, 1); +		i2c_master_recv(client, buf, 1); + +		if (!(buf[0] & 0x20)) +			break; +		if (time_after(jiffies,timeout)) { +			status = -ETIMEDOUT; +			break; +		} + +		msleep(10); +	} + +	/* delay a bit to let encoder settle */ +	msleep(50); + +	return status; +} + + +static inline void set_reg8(struct i2c_client *client, uint8_t reg, uint8_t val) +{ +	u8 buf[2]; + +	buf[0] = reg; +	buf[1] = val; +	i2c_master_send(client, buf, 2); +} + +static inline void set_reg16(struct i2c_client *client, uint8_t reg, uint16_t val) +{ +	u8 buf[3]; + +	buf[0] = reg; +	buf[1] = val >> 8; +	buf[2] = val & 0xff; +	i2c_master_send(client, buf, 3); +} + +static int saa6752hs_set_bitrate(struct i2c_client *client, +				 struct saa6752hs_state *h) +{ +	struct saa6752hs_mpeg_params *params = &h->params; +	int tot_bitrate; +	int is_384k; + +	/* set the bitrate mode */ +	set_reg8(client, 0x71, +		params->vi_bitrate_mode != V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); + +	/* set the video bitrate */ +	if (params->vi_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) { +		/* set the target bitrate */ +		set_reg16(client, 0x80, params->vi_bitrate); + +		/* set the max bitrate */ +		set_reg16(client, 0x81, params->vi_bitrate_peak); +		tot_bitrate = params->vi_bitrate_peak; +	} else { +		/* set the target bitrate (no max bitrate for CBR) */ +		set_reg16(client, 0x81, params->vi_bitrate); +		tot_bitrate = params->vi_bitrate; +	} + +	/* set the audio encoding */ +	set_reg8(client, 0x93, +			params->au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3); + +	/* set the audio bitrate */ +	if (params->au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) +		is_384k = V4L2_MPEG_AUDIO_AC3_BITRATE_384K == params->au_ac3_bitrate; +	else +		is_384k = V4L2_MPEG_AUDIO_L2_BITRATE_384K == params->au_l2_bitrate; +	set_reg8(client, 0x94, is_384k); +	tot_bitrate += is_384k ? 384 : 256; + +	/* Note: the total max bitrate is determined by adding the video and audio +	   bitrates together and also adding an extra 768kbit/s to stay on the +	   safe side. If more control should be required, then an extra MPEG control +	   should be added. */ +	tot_bitrate += 768; +	if (tot_bitrate > MPEG_TOTAL_TARGET_BITRATE_MAX) +		tot_bitrate = MPEG_TOTAL_TARGET_BITRATE_MAX; + +	/* set the total bitrate */ +	set_reg16(client, 0xb1, tot_bitrate); +	return 0; +} + +static int saa6752hs_try_ctrl(struct v4l2_ctrl *ctrl) +{ +	struct saa6752hs_state *h = +		container_of(ctrl->handler, struct saa6752hs_state, hdl); + +	switch (ctrl->id) { +	case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: +		/* peak bitrate shall be >= normal bitrate */ +		if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && +		    h->video_bitrate_peak->val < h->video_bitrate->val) +			h->video_bitrate_peak->val = h->video_bitrate->val; +		break; +	} +	return 0; +} + +static int saa6752hs_s_ctrl(struct v4l2_ctrl *ctrl) +{ +	struct saa6752hs_state *h = +		container_of(ctrl->handler, struct saa6752hs_state, hdl); +	struct saa6752hs_mpeg_params *params = &h->params; + +	switch (ctrl->id) { +	case V4L2_CID_MPEG_STREAM_TYPE: +		break; +	case V4L2_CID_MPEG_STREAM_PID_PMT: +		params->ts_pid_pmt = ctrl->val; +		break; +	case V4L2_CID_MPEG_STREAM_PID_AUDIO: +		params->ts_pid_audio = ctrl->val; +		break; +	case V4L2_CID_MPEG_STREAM_PID_VIDEO: +		params->ts_pid_video = ctrl->val; +		break; +	case V4L2_CID_MPEG_STREAM_PID_PCR: +		params->ts_pid_pcr = ctrl->val; +		break; +	case V4L2_CID_MPEG_AUDIO_ENCODING: +		params->au_encoding = ctrl->val; +		break; +	case V4L2_CID_MPEG_AUDIO_L2_BITRATE: +		params->au_l2_bitrate = ctrl->val; +		break; +	case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: +		params->au_ac3_bitrate = ctrl->val; +		break; +	case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: +		break; +	case V4L2_CID_MPEG_VIDEO_ENCODING: +		break; +	case V4L2_CID_MPEG_VIDEO_ASPECT: +		params->vi_aspect = ctrl->val; +		break; +	case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: +		params->vi_bitrate_mode = ctrl->val; +		params->vi_bitrate = h->video_bitrate->val / 1000; +		params->vi_bitrate_peak = h->video_bitrate_peak->val / 1000; +		v4l2_ctrl_activate(h->video_bitrate_peak, +				ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); +		break; +	default: +		return -EINVAL; +	} +	return 0; +} + +static int saa6752hs_init(struct v4l2_subdev *sd, u32 leading_null_bytes) +{ +	unsigned char buf[9], buf2[4]; +	struct saa6752hs_state *h = to_state(sd); +	struct i2c_client *client = v4l2_get_subdevdata(sd); +	unsigned size; +	u32 crc; +	unsigned char localPAT[256]; +	unsigned char localPMT[256]; + +	/* Set video format - must be done first as it resets other settings */ +	set_reg8(client, 0x41, h->video_format); + +	/* Set number of lines in input signal */ +	set_reg8(client, 0x40, (h->standard & V4L2_STD_525_60) ? 1 : 0); + +	/* set bitrate */ +	saa6752hs_set_bitrate(client, h); + +	/* Set GOP structure {3, 13} */ +	set_reg16(client, 0x72, 0x030d); + +	/* Set minimum Q-scale {4} */ +	set_reg8(client, 0x82, 0x04); + +	/* Set maximum Q-scale {12} */ +	set_reg8(client, 0x83, 0x0c); + +	/* Set Output Protocol */ +	set_reg8(client, 0xd0, 0x81); + +	/* Set video output stream format {TS} */ +	set_reg8(client, 0xb0, 0x05); + +	/* Set leading null byte for TS */ +	set_reg16(client, 0xf6, leading_null_bytes); + +	/* compute PAT */ +	memcpy(localPAT, PAT, sizeof(PAT)); +	localPAT[17] = 0xe0 | ((h->params.ts_pid_pmt >> 8) & 0x0f); +	localPAT[18] = h->params.ts_pid_pmt & 0xff; +	crc = crc32_be(~0, &localPAT[7], sizeof(PAT) - 7 - 4); +	localPAT[sizeof(PAT) - 4] = (crc >> 24) & 0xFF; +	localPAT[sizeof(PAT) - 3] = (crc >> 16) & 0xFF; +	localPAT[sizeof(PAT) - 2] = (crc >> 8) & 0xFF; +	localPAT[sizeof(PAT) - 1] = crc & 0xFF; + +	/* compute PMT */ +	if (h->params.au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) { +		size = sizeof(PMT_AC3); +		memcpy(localPMT, PMT_AC3, size); +	} else { +		size = sizeof(PMT); +		memcpy(localPMT, PMT, size); +	} +	localPMT[3] = 0x40 | ((h->params.ts_pid_pmt >> 8) & 0x0f); +	localPMT[4] = h->params.ts_pid_pmt & 0xff; +	localPMT[15] = 0xE0 | ((h->params.ts_pid_pcr >> 8) & 0x0F); +	localPMT[16] = h->params.ts_pid_pcr & 0xFF; +	localPMT[20] = 0xE0 | ((h->params.ts_pid_video >> 8) & 0x0F); +	localPMT[21] = h->params.ts_pid_video & 0xFF; +	localPMT[25] = 0xE0 | ((h->params.ts_pid_audio >> 8) & 0x0F); +	localPMT[26] = h->params.ts_pid_audio & 0xFF; +	crc = crc32_be(~0, &localPMT[7], size - 7 - 4); +	localPMT[size - 4] = (crc >> 24) & 0xFF; +	localPMT[size - 3] = (crc >> 16) & 0xFF; +	localPMT[size - 2] = (crc >> 8) & 0xFF; +	localPMT[size - 1] = crc & 0xFF; + +	/* Set Audio PID */ +	set_reg16(client, 0xc1, h->params.ts_pid_audio); + +	/* Set Video PID */ +	set_reg16(client, 0xc0, h->params.ts_pid_video); + +	/* Set PCR PID */ +	set_reg16(client, 0xc4, h->params.ts_pid_pcr); + +	/* Send SI tables */ +	i2c_master_send(client, localPAT, sizeof(PAT)); +	i2c_master_send(client, localPMT, size); + +	/* mute then unmute audio. This removes buzzing artefacts */ +	set_reg8(client, 0xa4, 1); +	set_reg8(client, 0xa4, 0); + +	/* start it going */ +	saa6752hs_chip_command(client, SAA6752HS_COMMAND_START); + +	/* readout current state */ +	buf[0] = 0xE1; +	buf[1] = 0xA7; +	buf[2] = 0xFE; +	buf[3] = 0x82; +	buf[4] = 0xB0; +	i2c_master_send(client, buf, 5); +	i2c_master_recv(client, buf2, 4); + +	/* change aspect ratio */ +	buf[0] = 0xE0; +	buf[1] = 0xA7; +	buf[2] = 0xFE; +	buf[3] = 0x82; +	buf[4] = 0xB0; +	buf[5] = buf2[0]; +	switch (h->params.vi_aspect) { +	case V4L2_MPEG_VIDEO_ASPECT_16x9: +		buf[6] = buf2[1] | 0x40; +		break; +	case V4L2_MPEG_VIDEO_ASPECT_4x3: +	default: +		buf[6] = buf2[1] & 0xBF; +		break; +	} +	buf[7] = buf2[2]; +	buf[8] = buf2[3]; +	i2c_master_send(client, buf, 9); + +	return 0; +} + +static int saa6752hs_g_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) +{ +	struct saa6752hs_state *h = to_state(sd); + +	if (h->video_format == SAA6752HS_VF_UNKNOWN) +		h->video_format = SAA6752HS_VF_D1; +	f->width = v4l2_format_table[h->video_format].fmt.pix.width; +	f->height = v4l2_format_table[h->video_format].fmt.pix.height; +	f->code = V4L2_MBUS_FMT_FIXED; +	f->field = V4L2_FIELD_INTERLACED; +	f->colorspace = V4L2_COLORSPACE_SMPTE170M; +	return 0; +} + +static int saa6752hs_try_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) +{ +	int dist_352, dist_480, dist_720; + +	f->code = V4L2_MBUS_FMT_FIXED; + +	dist_352 = abs(f->width - 352); +	dist_480 = abs(f->width - 480); +	dist_720 = abs(f->width - 720); +	if (dist_720 < dist_480) { +		f->width = 720; +		f->height = 576; +	} else if (dist_480 < dist_352) { +		f->width = 480; +		f->height = 576; +	} else { +		f->width = 352; +		if (abs(f->height - 576) < abs(f->height - 288)) +			f->height = 576; +		else +			f->height = 288; +	} +	f->field = V4L2_FIELD_INTERLACED; +	f->colorspace = V4L2_COLORSPACE_SMPTE170M; +	return 0; +} + +static int saa6752hs_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) +{ +	struct saa6752hs_state *h = to_state(sd); + +	if (f->code != V4L2_MBUS_FMT_FIXED) +		return -EINVAL; + +	/* +	  FIXME: translate and round width/height into EMPRESS +	  subsample type: + +	  type   |   PAL   |  NTSC +	  --------------------------- +	  SIF    | 352x288 | 352x240 +	  1/2 D1 | 352x576 | 352x480 +	  2/3 D1 | 480x576 | 480x480 +	  D1     | 720x576 | 720x480 +	*/ + +	saa6752hs_try_mbus_fmt(sd, f); +	if (f->width == 720) +		h->video_format = SAA6752HS_VF_D1; +	else if (f->width == 480) +		h->video_format = SAA6752HS_VF_2_3_D1; +	else if (f->height == 576) +		h->video_format = SAA6752HS_VF_1_2_D1; +	else +		h->video_format = SAA6752HS_VF_SIF; +	return 0; +} + +static int saa6752hs_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ +	struct saa6752hs_state *h = to_state(sd); + +	h->standard = std; +	return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops saa6752hs_ctrl_ops = { +	.try_ctrl = saa6752hs_try_ctrl, +	.s_ctrl = saa6752hs_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops saa6752hs_core_ops = { +	.init = saa6752hs_init, +}; + +static const struct v4l2_subdev_video_ops saa6752hs_video_ops = { +	.s_std = saa6752hs_s_std, +	.s_mbus_fmt = saa6752hs_s_mbus_fmt, +	.try_mbus_fmt = saa6752hs_try_mbus_fmt, +	.g_mbus_fmt = saa6752hs_g_mbus_fmt, +}; + +static const struct v4l2_subdev_ops saa6752hs_ops = { +	.core = &saa6752hs_core_ops, +	.video = &saa6752hs_video_ops, +}; + +static int saa6752hs_probe(struct i2c_client *client, +		const struct i2c_device_id *id) +{ +	struct saa6752hs_state *h = kzalloc(sizeof(*h), GFP_KERNEL); +	struct v4l2_subdev *sd; +	struct v4l2_ctrl_handler *hdl; +	u8 addr = 0x13; +	u8 data[12]; + +	v4l_info(client, "chip found @ 0x%x (%s)\n", +			client->addr << 1, client->adapter->name); +	if (h == NULL) +		return -ENOMEM; +	sd = &h->sd; +	v4l2_i2c_subdev_init(sd, client, &saa6752hs_ops); + +	i2c_master_send(client, &addr, 1); +	i2c_master_recv(client, data, sizeof(data)); +	h->revision = (data[8] << 8) | data[9]; +	h->has_ac3 = 0; +	if (h->revision == 0x0206) { +		h->has_ac3 = 1; +		v4l_info(client, "supports AC-3\n"); +	} +	h->params = param_defaults; + +	hdl = &h->hdl; +	v4l2_ctrl_handler_init(hdl, 14); +	v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops, +		V4L2_CID_MPEG_AUDIO_ENCODING, +		h->has_ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3 : +			V4L2_MPEG_AUDIO_ENCODING_LAYER_2, +		0x0d, V4L2_MPEG_AUDIO_ENCODING_LAYER_2); + +	v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops, +		V4L2_CID_MPEG_AUDIO_L2_BITRATE, +		V4L2_MPEG_AUDIO_L2_BITRATE_384K, +		~((1 << V4L2_MPEG_AUDIO_L2_BITRATE_256K) | +		  (1 << V4L2_MPEG_AUDIO_L2_BITRATE_384K)), +		V4L2_MPEG_AUDIO_L2_BITRATE_256K); + +	if (h->has_ac3) +		v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops, +			V4L2_CID_MPEG_AUDIO_AC3_BITRATE, +			V4L2_MPEG_AUDIO_AC3_BITRATE_384K, +			~((1 << V4L2_MPEG_AUDIO_AC3_BITRATE_256K) | +			  (1 << V4L2_MPEG_AUDIO_AC3_BITRATE_384K)), +			V4L2_MPEG_AUDIO_AC3_BITRATE_256K); + +	v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops, +		V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, +		V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, +		~(1 << V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000), +		V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000); + +	v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops, +		V4L2_CID_MPEG_VIDEO_ENCODING, +		V4L2_MPEG_VIDEO_ENCODING_MPEG_2, +		~(1 << V4L2_MPEG_VIDEO_ENCODING_MPEG_2), +		V4L2_MPEG_VIDEO_ENCODING_MPEG_2); + +	v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops, +		V4L2_CID_MPEG_VIDEO_ASPECT, +		V4L2_MPEG_VIDEO_ASPECT_16x9, 0x01, +		V4L2_MPEG_VIDEO_ASPECT_4x3); + +	h->video_bitrate_peak = v4l2_ctrl_new_std(hdl, &saa6752hs_ctrl_ops, +		V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, +		1000000, 27000000, 1000, 8000000); + +	v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops, +		V4L2_CID_MPEG_STREAM_TYPE, +		V4L2_MPEG_STREAM_TYPE_MPEG2_TS, +		~(1 << V4L2_MPEG_STREAM_TYPE_MPEG2_TS), +		V4L2_MPEG_STREAM_TYPE_MPEG2_TS); + +	h->video_bitrate_mode = v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops, +		V4L2_CID_MPEG_VIDEO_BITRATE_MODE, +		V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0, +		V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); +	h->video_bitrate = v4l2_ctrl_new_std(hdl, &saa6752hs_ctrl_ops, +		V4L2_CID_MPEG_VIDEO_BITRATE, 1000000, 27000000, 1000, 6000000); +	v4l2_ctrl_new_std(hdl, &saa6752hs_ctrl_ops, +		V4L2_CID_MPEG_STREAM_PID_PMT, 0, (1 << 14) - 1, 1, 16); +	v4l2_ctrl_new_std(hdl, &saa6752hs_ctrl_ops, +		V4L2_CID_MPEG_STREAM_PID_AUDIO, 0, (1 << 14) - 1, 1, 260); +	v4l2_ctrl_new_std(hdl, &saa6752hs_ctrl_ops, +		V4L2_CID_MPEG_STREAM_PID_VIDEO, 0, (1 << 14) - 1, 1, 256); +	v4l2_ctrl_new_std(hdl, &saa6752hs_ctrl_ops, +		V4L2_CID_MPEG_STREAM_PID_PCR, 0, (1 << 14) - 1, 1, 259); +	sd->ctrl_handler = hdl; +	if (hdl->error) { +		int err = hdl->error; + +		v4l2_ctrl_handler_free(hdl); +		kfree(h); +		return err; +	} +	v4l2_ctrl_cluster(3, &h->video_bitrate_mode); +	v4l2_ctrl_handler_setup(hdl); +	h->standard = 0; /* Assume 625 input lines */ +	return 0; +} + +static int saa6752hs_remove(struct i2c_client *client) +{ +	struct v4l2_subdev *sd = i2c_get_clientdata(client); + +	v4l2_device_unregister_subdev(sd); +	v4l2_ctrl_handler_free(&to_state(sd)->hdl); +	kfree(to_state(sd)); +	return 0; +} + +static const struct i2c_device_id saa6752hs_id[] = { +	{ "saa6752hs", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, saa6752hs_id); + +static struct i2c_driver saa6752hs_driver = { +	.driver = { +		.owner	= THIS_MODULE, +		.name	= "saa6752hs", +	}, +	.probe		= saa6752hs_probe, +	.remove		= saa6752hs_remove, +	.id_table	= saa6752hs_id, +}; + +module_i2c_driver(saa6752hs_driver); diff --git a/drivers/media/i2c/saa7110.c b/drivers/media/i2c/saa7110.c index ac43e929a1d..99689ee57d7 100644 --- a/drivers/media/i2c/saa7110.c +++ b/drivers/media/i2c/saa7110.c @@ -365,10 +365,10 @@ static const struct v4l2_subdev_core_ops saa7110_core_ops = {  	.s_ctrl = v4l2_subdev_s_ctrl,  	.queryctrl = v4l2_subdev_queryctrl,  	.querymenu = v4l2_subdev_querymenu, -	.s_std = saa7110_s_std,  };  static const struct v4l2_subdev_video_ops saa7110_video_ops = { +	.s_std = saa7110_s_std,  	.s_routing = saa7110_s_routing,  	.s_stream = saa7110_s_stream,  	.querystd = saa7110_querystd, diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c index 637d0263452..35a44648150 100644 --- a/drivers/media/i2c/saa7115.c +++ b/drivers/media/i2c/saa7115.c @@ -1582,7 +1582,6 @@ static const struct v4l2_subdev_core_ops saa711x_core_ops = {  	.s_ctrl = v4l2_subdev_s_ctrl,  	.queryctrl = v4l2_subdev_queryctrl,  	.querymenu = v4l2_subdev_querymenu, -	.s_std = saa711x_s_std,  	.reset = saa711x_reset,  	.s_gpio = saa711x_s_gpio,  #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -1601,6 +1600,7 @@ static const struct v4l2_subdev_audio_ops saa711x_audio_ops = {  };  static const struct v4l2_subdev_video_ops saa711x_video_ops = { +	.s_std = saa711x_s_std,  	.s_routing = saa711x_s_routing,  	.s_crystal_freq = saa711x_s_crystal_freq,  	.s_mbus_fmt = saa711x_s_mbus_fmt, @@ -1699,7 +1699,7 @@ static void saa711x_write_platform_data(struct saa711x_state *state,   * the analog demod.   * If the tuner is not found, it returns -ENODEV.   * If auto-detection is disabled and the tuner doesn't match what it was - *	requred, it returns -EINVAL and fills 'name'. + *	required, it returns -EINVAL and fills 'name'.   * If the chip is found, it returns the chip ID and fills 'name'.   */  static int saa711x_detect_chip(struct i2c_client *client, diff --git a/drivers/media/i2c/saa717x.c b/drivers/media/i2c/saa717x.c index 401ca114ab9..6922a9f9a5c 100644 --- a/drivers/media/i2c/saa717x.c +++ b/drivers/media/i2c/saa717x.c @@ -1198,7 +1198,6 @@ static const struct v4l2_subdev_core_ops saa717x_core_ops = {  	.g_register = saa717x_g_register,  	.s_register = saa717x_s_register,  #endif -	.s_std = saa717x_s_std,  	.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,  	.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,  	.s_ext_ctrls = v4l2_subdev_s_ext_ctrls, @@ -1216,6 +1215,7 @@ static const struct v4l2_subdev_tuner_ops saa717x_tuner_ops = {  };  static const struct v4l2_subdev_video_ops saa717x_video_ops = { +	.s_std = saa717x_s_std,  	.s_routing = saa717x_s_video_routing,  	.s_mbus_fmt = saa717x_s_mbus_fmt,  	.s_stream = saa717x_s_stream, diff --git a/drivers/media/i2c/saa7191.c b/drivers/media/i2c/saa7191.c index 606a4baf944..8e9699268a6 100644 --- a/drivers/media/i2c/saa7191.c +++ b/drivers/media/i2c/saa7191.c @@ -573,10 +573,10 @@ static int saa7191_g_input_status(struct v4l2_subdev *sd, u32 *status)  static const struct v4l2_subdev_core_ops saa7191_core_ops = {  	.g_ctrl = saa7191_g_ctrl,  	.s_ctrl = saa7191_s_ctrl, -	.s_std = saa7191_s_std,  };  static const struct v4l2_subdev_video_ops saa7191_video_ops = { +	.s_std = saa7191_s_std,  	.s_routing = saa7191_s_routing,  	.querystd = saa7191_querystd,  	.g_input_status = saa7191_g_input_status, diff --git a/drivers/media/i2c/smiapp-pll.h b/drivers/media/i2c/smiapp-pll.h index a4a649834a1..5ce2b61da3c 100644 --- a/drivers/media/i2c/smiapp-pll.h +++ b/drivers/media/i2c/smiapp-pll.h @@ -46,7 +46,7 @@ struct smiapp_pll {  			uint8_t bus_width;  		} parallel;  	}; -	uint8_t flags; +	unsigned long flags;  	uint8_t binning_horizontal;  	uint8_t binning_vertical;  	uint8_t scale_m; diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index ae66d91bf71..06fb03291d5 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -399,7 +399,6 @@ static void smiapp_update_mbus_formats(struct smiapp_sensor *sensor)  	BUG_ON(max(internal_csi_format_idx, csi_format_idx) + pixel_order  	       >= ARRAY_SIZE(smiapp_csi_data_formats)); -	BUG_ON(min(internal_csi_format_idx, csi_format_idx) < 0);  	dev_dbg(&client->dev, "new pixel order %s\n",  		pixel_order_str[pixel_order]); @@ -607,7 +606,7 @@ static int smiapp_get_limits(struct smiapp_sensor *sensor, int const *limit,  		if (rval)  			return rval;  		sensor->limits[limit[i]] = val; -		dev_dbg(&client->dev, "0x%8.8x \"%s\" = %d, 0x%x\n", +		dev_dbg(&client->dev, "0x%8.8x \"%s\" = %u, 0x%x\n",  			smiapp_reg_limits[limit[i]].addr,  			smiapp_reg_limits[limit[i]].what, val, val);  	} @@ -742,8 +741,8 @@ static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor)  		if (rval)  			return rval; -		dev_dbg(&client->dev, "bpp %d, compressed %d\n", -			fmt >> 8, (u8)fmt); +		dev_dbg(&client->dev, "%u: bpp %u, compressed %u\n", +			i, fmt >> 8, (u8)fmt);  		for (j = 0; j < ARRAY_SIZE(smiapp_csi_data_formats); j++) {  			const struct smiapp_csi_data_format *f = @@ -1129,7 +1128,7 @@ static int smiapp_power_on(struct smiapp_sensor *sensor)  	}  	usleep_range(1000, 1000); -	if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) +	if (gpio_is_valid(sensor->platform_data->xshutdown))  		gpio_set_value(sensor->platform_data->xshutdown, 1);  	sleep = SMIAPP_RESET_DELAY(sensor->platform_data->ext_clk); @@ -1239,7 +1238,7 @@ static int smiapp_power_on(struct smiapp_sensor *sensor)  	return 0;  out_cci_addr_fail: -	if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) +	if (gpio_is_valid(sensor->platform_data->xshutdown))  		gpio_set_value(sensor->platform_data->xshutdown, 0);  	if (sensor->platform_data->set_xclk)  		sensor->platform_data->set_xclk(&sensor->src->sd, 0); @@ -1265,7 +1264,7 @@ static void smiapp_power_off(struct smiapp_sensor *sensor)  			     SMIAPP_REG_U8_SOFTWARE_RESET,  			     SMIAPP_SOFTWARE_RESET); -	if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) +	if (gpio_is_valid(sensor->platform_data->xshutdown))  		gpio_set_value(sensor->platform_data->xshutdown, 0);  	if (sensor->platform_data->set_xclk)  		sensor->platform_data->set_xclk(&sensor->src->sd, 0); @@ -1767,7 +1766,7 @@ static void smiapp_set_compose_binner(struct v4l2_subdev *subdev,  	struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);  	unsigned int i;  	unsigned int binh = 1, binv = 1; -	unsigned int best = scaling_goodness( +	int best = scaling_goodness(  		subdev,  		crops[SMIAPP_PAD_SINK]->width, sel->r.width,  		crops[SMIAPP_PAD_SINK]->height, sel->r.height, sel->flags); @@ -2028,8 +2027,8 @@ static int smiapp_set_crop(struct v4l2_subdev *subdev,  	sel->r.width = min(sel->r.width, src_size->width);  	sel->r.height = min(sel->r.height, src_size->height); -	sel->r.left = min(sel->r.left, src_size->width - sel->r.width); -	sel->r.top = min(sel->r.top, src_size->height - sel->r.height); +	sel->r.left = min_t(int, sel->r.left, src_size->width - sel->r.width); +	sel->r.top = min_t(int, sel->r.top, src_size->height - sel->r.height);  	*crops[sel->pad] = sel->r; @@ -2121,8 +2120,8 @@ static int smiapp_set_selection(struct v4l2_subdev *subdev,  	sel->r.left = max(0, sel->r.left & ~1);  	sel->r.top = max(0, sel->r.top & ~1); -	sel->r.width = max(0, SMIAPP_ALIGN_DIM(sel->r.width, sel->flags)); -	sel->r.height = max(0, SMIAPP_ALIGN_DIM(sel->r.height, sel->flags)); +	sel->r.width = SMIAPP_ALIGN_DIM(sel->r.width, sel->flags); +	sel->r.height =	SMIAPP_ALIGN_DIM(sel->r.height, sel->flags);  	sel->r.width = max_t(unsigned int,  			     sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE], @@ -2356,17 +2355,17 @@ static int smiapp_registered(struct v4l2_subdev *subdev)  	unsigned int i;  	int rval; -	sensor->vana = devm_regulator_get(&client->dev, "VANA"); +	sensor->vana = devm_regulator_get(&client->dev, "vana");  	if (IS_ERR(sensor->vana)) {  		dev_err(&client->dev, "could not get regulator for vana\n"); -		return -ENODEV; +		return PTR_ERR(sensor->vana);  	}  	if (!sensor->platform_data->set_xclk) {  		sensor->ext_clk = devm_clk_get(&client->dev, "ext_clk");  		if (IS_ERR(sensor->ext_clk)) {  			dev_err(&client->dev, "could not get clock\n"); -			return -ENODEV; +			return PTR_ERR(sensor->ext_clk);  		}  		rval = clk_set_rate(sensor->ext_clk, @@ -2375,18 +2374,19 @@ static int smiapp_registered(struct v4l2_subdev *subdev)  			dev_err(&client->dev,  				"unable to set clock freq to %u\n",  				sensor->platform_data->ext_clk); -			return -ENODEV; +			return rval;  		}  	} -	if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) { -		if (devm_gpio_request_one(&client->dev, -					  sensor->platform_data->xshutdown, 0, -					  "SMIA++ xshutdown") != 0) { +	if (gpio_is_valid(sensor->platform_data->xshutdown)) { +		rval = devm_gpio_request_one( +			&client->dev, sensor->platform_data->xshutdown, 0, +			"SMIA++ xshutdown"); +		if (rval < 0) {  			dev_err(&client->dev,  				"unable to acquire reset gpio %d\n",  				sensor->platform_data->xshutdown); -			return -ENODEV; +			return rval;  		}  	} @@ -2424,6 +2424,12 @@ static int smiapp_registered(struct v4l2_subdev *subdev)  		sensor->hvflip_inv_mask = SMIAPP_IMAGE_ORIENTATION_HFLIP |  					  SMIAPP_IMAGE_ORIENTATION_VFLIP; +	rval = smiapp_call_quirk(sensor, limits); +	if (rval) { +		dev_err(&client->dev, "limits quirks failed\n"); +		goto out_power_off; +	} +  	rval = smiapp_get_mbus_formats(sensor);  	if (rval) {  		rval = -ENODEV; @@ -2484,12 +2490,6 @@ static int smiapp_registered(struct v4l2_subdev *subdev)  		}  	} -	rval = smiapp_call_quirk(sensor, limits); -	if (rval) { -		dev_err(&client->dev, "limits quirks failed\n"); -		goto out_nvm_release; -	} -  	/* We consider this as profile 0 sensor if any of these are zero. */  	if (!sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV] ||  	    !sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV] || @@ -2544,8 +2544,9 @@ static int smiapp_registered(struct v4l2_subdev *subdev)  		}  		snprintf(this->sd.name, -			 sizeof(this->sd.name), "%s %s", -			 sensor->minfo.name, _this->name); +			 sizeof(this->sd.name), "%s %d-%4.4x %s", +			 sensor->minfo.name, i2c_adapter_id(client->adapter), +			 client->addr, _this->name);  		this->sink_fmt.width =  			sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; @@ -2617,12 +2618,11 @@ static int smiapp_registered(struct v4l2_subdev *subdev)  	pll->bus_type = SMIAPP_PLL_BUS_TYPE_CSI2;  	pll->csi2.lanes = sensor->platform_data->lanes;  	pll->ext_clk_freq_hz = sensor->platform_data->ext_clk; +	pll->flags = smiapp_call_quirk(sensor, pll_flags); +  	/* Profile 0 sensors have no separate OP clock branch. */  	if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0)  		pll->flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS; -	if (smiapp_needs_quirk(sensor, -			       SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE)) -		pll->flags |= SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE;  	pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN];  	rval = smiapp_update_mode(sensor); @@ -2831,7 +2831,7 @@ static int smiapp_remove(struct i2c_client *client)  	unsigned int i;  	if (sensor->power_count) { -		if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) +		if (gpio_is_valid(sensor->platform_data->xshutdown))  			gpio_set_value(sensor->platform_data->xshutdown, 0);  		if (sensor->platform_data->set_xclk)  			sensor->platform_data->set_xclk(&sensor->src->sd, 0); diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.c b/drivers/media/i2c/smiapp/smiapp-quirk.c index bb8c506e0e3..e0bee875212 100644 --- a/drivers/media/i2c/smiapp/smiapp-quirk.c +++ b/drivers/media/i2c/smiapp/smiapp-quirk.c @@ -28,7 +28,7 @@  static int smiapp_write_8(struct smiapp_sensor *sensor, u16 reg, u8 val)  { -	return smiapp_write(sensor, (SMIA_REG_8BIT << 16) | reg, val); +	return smiapp_write(sensor, SMIAPP_REG_MK_U8(reg), val);  }  static int smiapp_write_8s(struct smiapp_sensor *sensor, @@ -61,52 +61,6 @@ void smiapp_replace_limit(struct smiapp_sensor *sensor,  	sensor->limits[limit] = val;  } -bool smiapp_quirk_reg(struct smiapp_sensor *sensor, -		      u32 reg, u32 *val) -{ -	struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); -	const struct smia_reg *sreg; - -	if (!sensor->minfo.quirk) -		return false; - -	sreg = sensor->minfo.quirk->regs; - -	if (!sreg) -		return false; - -	while (sreg->type) { -		u16 type = reg >> 16; -		u16 reg16 = reg; - -		if (sreg->type != type || sreg->reg != reg16) { -			sreg++; -			continue; -		} - -		switch ((u8)type) { -		case SMIA_REG_8BIT: -			dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%2.2x\n", -				reg, sreg->val); -			break; -		case SMIA_REG_16BIT: -			dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%4.4x\n", -				reg, sreg->val); -			break; -		case SMIA_REG_32BIT: -			dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%8.8x\n", -				reg, sreg->val); -			break; -		} - -		*val = sreg->val; - -		return true; -	} - -	return false; -} -  static int jt8ew9_limits(struct smiapp_sensor *sensor)  {  	if (sensor->minfo.revision_number_major < 0x03) @@ -266,12 +220,17 @@ static int jt8ev1_post_streamoff(struct smiapp_sensor *sensor)  	return smiapp_write_8(sensor, 0x3328, 0x80);  } +static unsigned long jt8ev1_pll_flags(struct smiapp_sensor *sensor) +{ +	return SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE; +} +  const struct smiapp_quirk smiapp_jt8ev1_quirk = {  	.limits = jt8ev1_limits,  	.post_poweron = jt8ev1_post_poweron,  	.pre_streamon = jt8ev1_pre_streamon,  	.post_streamoff = jt8ev1_post_streamoff, -	.flags = SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE, +	.pll_flags = jt8ev1_pll_flags,  };  static int tcm8500md_limits(struct smiapp_sensor *sensor) diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.h b/drivers/media/i2c/smiapp/smiapp-quirk.h index 504a6d80ced..46e9ea8bfa0 100644 --- a/drivers/media/i2c/smiapp/smiapp-quirk.h +++ b/drivers/media/i2c/smiapp/smiapp-quirk.h @@ -35,19 +35,30 @@ struct smiapp_sensor;   * @post_poweron: Called always after the sensor has been fully powered on.   * @pre_streamon: Called just before streaming is enabled.   * @post_streamon: Called right after stopping streaming. + * @reg_access: Register access quirk. The quirk may divert the access + *		to another register, or no register at all. + * + *		@write: Is this read (false) or write (true) access? + *		@reg: Pointer to the register to access + *		@value: Register value, set by the caller on write, or + *			by the quirk on read + * + *		@return: 0 on success, -ENOIOCTLCMD if no register + *			 access may be done by the caller (default read + *			 value is zero), else negative error code on error   */  struct smiapp_quirk {  	int (*limits)(struct smiapp_sensor *sensor);  	int (*post_poweron)(struct smiapp_sensor *sensor);  	int (*pre_streamon)(struct smiapp_sensor *sensor);  	int (*post_streamoff)(struct smiapp_sensor *sensor); -	const struct smia_reg *regs; +	unsigned long (*pll_flags)(struct smiapp_sensor *sensor); +	int (*reg_access)(struct smiapp_sensor *sensor, bool write, u32 *reg, +			  u32 *val);  	unsigned long flags;  }; -/* op pix clock is for all lanes in total normally */ -#define SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE			(1 << 0) -#define SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY			(1 << 1) +#define SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY			(1 << 0)  struct smiapp_reg_8 {  	u16 reg; @@ -56,12 +67,9 @@ struct smiapp_reg_8 {  void smiapp_replace_limit(struct smiapp_sensor *sensor,  			  u32 limit, u32 val); -bool smiapp_quirk_reg(struct smiapp_sensor *sensor, -		      u32 reg, u32 *val); -#define SMIAPP_MK_QUIRK_REG(_reg, _val) \ +#define SMIAPP_MK_QUIRK_REG_8(_reg, _val) \  	{				\ -		.type = (_reg >> 16),	\  		.reg = (u16)_reg,	\  		.val = _val,		\  	} diff --git a/drivers/media/i2c/smiapp/smiapp-reg-defs.h b/drivers/media/i2c/smiapp/smiapp-reg-defs.h index 3aa0ca948d8..c488ef02807 100644 --- a/drivers/media/i2c/smiapp/smiapp-reg-defs.h +++ b/drivers/media/i2c/smiapp/smiapp-reg-defs.h @@ -21,11 +21,11 @@   * 02110-1301 USA   *   */ -#define SMIAPP_REG_MK_U8(r) ((SMIA_REG_8BIT << 16) | (r)) -#define SMIAPP_REG_MK_U16(r) ((SMIA_REG_16BIT << 16) | (r)) -#define SMIAPP_REG_MK_U32(r) ((SMIA_REG_32BIT << 16) | (r)) +#define SMIAPP_REG_MK_U8(r) ((SMIAPP_REG_8BIT << 16) | (r)) +#define SMIAPP_REG_MK_U16(r) ((SMIAPP_REG_16BIT << 16) | (r)) +#define SMIAPP_REG_MK_U32(r) ((SMIAPP_REG_32BIT << 16) | (r)) -#define SMIAPP_REG_MK_F32(r) (SMIA_REG_FLAG_FLOAT | (SMIA_REG_32BIT << 16) | (r)) +#define SMIAPP_REG_MK_F32(r) (SMIAPP_REG_FLAG_FLOAT | (SMIAPP_REG_32BIT << 16) | (r))  #define SMIAPP_REG_U16_MODEL_ID					SMIAPP_REG_MK_U16(0x0000)  #define SMIAPP_REG_U8_REVISION_NUMBER_MAJOR			SMIAPP_REG_MK_U8(0x0002) diff --git a/drivers/media/i2c/smiapp/smiapp-regs.c b/drivers/media/i2c/smiapp/smiapp-regs.c index 4fac32cfcb3..a2098007fb7 100644 --- a/drivers/media/i2c/smiapp/smiapp-regs.c +++ b/drivers/media/i2c/smiapp/smiapp-regs.c @@ -114,14 +114,14 @@ static int ____smiapp_read(struct smiapp_sensor *sensor, u16 reg,  	*val = 0;  	/* high byte comes first */  	switch (len) { -	case SMIA_REG_32BIT: +	case SMIAPP_REG_32BIT:  		*val = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) +  			data[3];  		break; -	case SMIA_REG_16BIT: +	case SMIAPP_REG_16BIT:  		*val = (data[0] << 8) + data[1];  		break; -	case SMIA_REG_8BIT: +	case SMIAPP_REG_8BIT:  		*val = data[0];  		break;  	default: @@ -165,31 +165,28 @@ static int __smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val,  			 bool only8)  {  	struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); -	unsigned int len = (u8)(reg >> 16); +	u8 len = SMIAPP_REG_WIDTH(reg);  	int rval; -	if (len != SMIA_REG_8BIT && len != SMIA_REG_16BIT -	    && len != SMIA_REG_32BIT) +	if (len != SMIAPP_REG_8BIT && len != SMIAPP_REG_16BIT +	    && len != SMIAPP_REG_32BIT)  		return -EINVAL; -	if (smiapp_quirk_reg(sensor, reg, val)) -		goto found_quirk; - -	if (len == SMIA_REG_8BIT && !only8) -		rval = ____smiapp_read(sensor, (u16)reg, len, val); +	if (len == SMIAPP_REG_8BIT || !only8) +		rval = ____smiapp_read(sensor, SMIAPP_REG_ADDR(reg), len, val);  	else -		rval = ____smiapp_read_8only(sensor, (u16)reg, len, val); +		rval = ____smiapp_read_8only(sensor, SMIAPP_REG_ADDR(reg), len, +					     val);  	if (rval < 0)  		return rval; -found_quirk: -	if (reg & SMIA_REG_FLAG_FLOAT) +	if (reg & SMIAPP_REG_FLAG_FLOAT)  		*val = float_to_u32_mul_1000000(client, *val);  	return 0;  } -int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val) +int smiapp_read_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 *val)  {  	return __smiapp_read(  		sensor, reg, val, @@ -197,28 +194,47 @@ int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val)  				   SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY));  } +int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val) +{ +	int rval; + +	*val = 0; +	rval = smiapp_call_quirk(sensor, reg_access, false, ®, val); +	if (rval == -ENOIOCTLCMD) +		return 0; +	if (rval < 0) +		return rval; + +	return smiapp_read_no_quirk(sensor, reg, val); +} +  int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val)  { +	int rval; + +	*val = 0; +	rval = smiapp_call_quirk(sensor, reg_access, false, ®, val); +	if (rval == -ENOIOCTLCMD) +		return 0; +	if (rval < 0) +		return rval; +  	return __smiapp_read(sensor, reg, val, true);  } -/* - * Write to a 8/16-bit register. - * Returns zero if successful, or non-zero otherwise. - */ -int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val) +int smiapp_write_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 val)  {  	struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);  	struct i2c_msg msg;  	unsigned char data[6];  	unsigned int retries; -	unsigned int flags = reg >> 24; -	unsigned int len = (u8)(reg >> 16); -	u16 offset = reg; +	u8 flags = SMIAPP_REG_FLAGS(reg); +	u8 len = SMIAPP_REG_WIDTH(reg); +	u16 offset = SMIAPP_REG_ADDR(reg);  	int r; -	if ((len != SMIA_REG_8BIT && len != SMIA_REG_16BIT && -	     len != SMIA_REG_32BIT) || flags) +	if ((len != SMIAPP_REG_8BIT && len != SMIAPP_REG_16BIT && +	     len != SMIAPP_REG_32BIT) || flags)  		return -EINVAL;  	msg.addr = client->addr; @@ -231,14 +247,14 @@ int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val)  	data[1] = (u8) (reg & 0xff);  	switch (len) { -	case SMIA_REG_8BIT: +	case SMIAPP_REG_8BIT:  		data[2] = val;  		break; -	case SMIA_REG_16BIT: +	case SMIAPP_REG_16BIT:  		data[2] = val >> 8;  		data[3] = val;  		break; -	case SMIA_REG_32BIT: +	case SMIAPP_REG_32BIT:  		data[2] = val >> 24;  		data[3] = val >> 16;  		data[4] = val >> 8; @@ -271,3 +287,20 @@ int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val)  	return r;  } + +/* + * Write to a 8/16-bit register. + * Returns zero if successful, or non-zero otherwise. + */ +int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val) +{ +	int rval; + +	rval = smiapp_call_quirk(sensor, reg_access, true, ®, &val); +	if (rval == -ENOIOCTLCMD) +		return 0; +	if (rval < 0) +		return rval; + +	return smiapp_write_no_quirk(sensor, reg, val); +} diff --git a/drivers/media/i2c/smiapp/smiapp-regs.h b/drivers/media/i2c/smiapp/smiapp-regs.h index eefc6c84d5f..35521125a2c 100644 --- a/drivers/media/i2c/smiapp/smiapp-regs.h +++ b/drivers/media/i2c/smiapp/smiapp-regs.h @@ -28,22 +28,23 @@  #include <linux/i2c.h>  #include <linux/types.h> +#define SMIAPP_REG_ADDR(reg)		((u16)reg) +#define SMIAPP_REG_WIDTH(reg)		((u8)(reg >> 16)) +#define SMIAPP_REG_FLAGS(reg)		((u8)(reg >> 24)) +  /* Use upper 8 bits of the type field for flags */ -#define SMIA_REG_FLAG_FLOAT		(1 << 24) +#define SMIAPP_REG_FLAG_FLOAT		(1 << 24) -#define SMIA_REG_8BIT			1 -#define SMIA_REG_16BIT			2 -#define SMIA_REG_32BIT			4 -struct smia_reg { -	u16 type; -	u16 reg;			/* 16-bit offset */ -	u32 val;			/* 8/16/32-bit value */ -}; +#define SMIAPP_REG_8BIT			1 +#define SMIAPP_REG_16BIT		2 +#define SMIAPP_REG_32BIT		4  struct smiapp_sensor; +int smiapp_read_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 *val);  int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val);  int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val); +int smiapp_write_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 val);  int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val);  #endif diff --git a/drivers/media/i2c/soc_camera/imx074.c b/drivers/media/i2c/soc_camera/imx074.c index 1d384a371b4..5b915936c3f 100644 --- a/drivers/media/i2c/soc_camera/imx074.c +++ b/drivers/media/i2c/soc_camera/imx074.c @@ -451,7 +451,9 @@ static int imx074_probe(struct i2c_client *client,  	if (ret < 0)  		goto eprobe; -	return v4l2_async_register_subdev(&priv->subdev); +	ret = v4l2_async_register_subdev(&priv->subdev); +	if (!ret) +		return 0;  epwrinit:  eprobe: diff --git a/drivers/media/i2c/soc_camera/mt9m111.c b/drivers/media/i2c/soc_camera/mt9m111.c index 6f4056668bb..ccf59406a17 100644 --- a/drivers/media/i2c/soc_camera/mt9m111.c +++ b/drivers/media/i2c/soc_camera/mt9m111.c @@ -208,8 +208,8 @@ struct mt9m111 {  	struct mt9m111_context *ctx;  	struct v4l2_rect rect;	/* cropping rectangle */  	struct v4l2_clk *clk; -	int width;		/* output */ -	int height;		/* sizes */ +	unsigned int width;	/* output */ +	unsigned int height;	/* sizes */  	struct mutex power_lock; /* lock to protect power_count */  	int power_count;  	const struct mt9m111_datafmt *fmt; diff --git a/drivers/media/i2c/soc_camera/ov5642.c b/drivers/media/i2c/soc_camera/ov5642.c index 0a5c5d4fedd..d2daa6a8f27 100644 --- a/drivers/media/i2c/soc_camera/ov5642.c +++ b/drivers/media/i2c/soc_camera/ov5642.c @@ -642,7 +642,7 @@ static const struct ov5642_datafmt  static int reg_read(struct i2c_client *client, u16 reg, u8 *val)  {  	int ret; -	/* We have 16-bit i2c addresses - care for endianess */ +	/* We have 16-bit i2c addresses - care for endianness */  	unsigned char data[2] = { reg >> 8, reg & 0xff };  	ret = i2c_master_send(client, data, 2); diff --git a/drivers/media/i2c/soc_camera/ov9640.c b/drivers/media/i2c/soc_camera/ov9640.c index e968c3fdbd9..bc74224503e 100644 --- a/drivers/media/i2c/soc_camera/ov9640.c +++ b/drivers/media/i2c/soc_camera/ov9640.c @@ -371,7 +371,7 @@ static void ov9640_alter_regs(enum v4l2_mbus_pixelcode code,  		alt->com13	= OV9640_COM13_RGB_AVG;  		alt->com15	= OV9640_COM15_RGB_565;  		break; -	}; +	}  }  /* Setup registers according to resolution and color encoding */ diff --git a/drivers/media/i2c/soc_camera/tw9910.c b/drivers/media/i2c/soc_camera/tw9910.c index ab54628d941..416402eb4f8 100644 --- a/drivers/media/i2c/soc_camera/tw9910.c +++ b/drivers/media/i2c/soc_camera/tw9910.c @@ -814,8 +814,6 @@ done:  }  static struct v4l2_subdev_core_ops tw9910_subdev_core_ops = { -	.s_std		= tw9910_s_std, -	.g_std		= tw9910_g_std,  #ifdef CONFIG_VIDEO_ADV_DEBUG  	.g_register	= tw9910_g_register,  	.s_register	= tw9910_s_register, @@ -872,7 +870,15 @@ static int tw9910_s_mbus_config(struct v4l2_subdev *sd,  	return i2c_smbus_write_byte_data(client, OUTCTR1, val);  } +static int tw9910_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm) +{ +	*norm = V4L2_STD_NTSC | V4L2_STD_PAL; +	return 0; +} +  static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = { +	.s_std		= tw9910_s_std, +	.g_std		= tw9910_g_std,  	.s_stream	= tw9910_s_stream,  	.g_mbus_fmt	= tw9910_g_fmt,  	.s_mbus_fmt	= tw9910_s_fmt, @@ -882,6 +888,7 @@ static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = {  	.enum_mbus_fmt	= tw9910_enum_fmt,  	.g_mbus_config	= tw9910_g_mbus_config,  	.s_mbus_config	= tw9910_s_mbus_config, +	.g_tvnorms	= tw9910_g_tvnorms,  };  static struct v4l2_subdev_ops tw9910_subdev_ops = { diff --git a/drivers/media/i2c/sony-btf-mpx.c b/drivers/media/i2c/sony-btf-mpx.c index 32d82320b48..1da8004f5a8 100644 --- a/drivers/media/i2c/sony-btf-mpx.c +++ b/drivers/media/i2c/sony-btf-mpx.c @@ -327,18 +327,18 @@ static int sony_btf_mpx_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner  /* --------------------------------------------------------------------------*/ -static const struct v4l2_subdev_core_ops sony_btf_mpx_core_ops = { -	.s_std = sony_btf_mpx_s_std, -}; -  static const struct v4l2_subdev_tuner_ops sony_btf_mpx_tuner_ops = {  	.s_tuner = sony_btf_mpx_s_tuner,  	.g_tuner = sony_btf_mpx_g_tuner,  }; +static const struct v4l2_subdev_video_ops sony_btf_mpx_video_ops = { +	.s_std = sony_btf_mpx_s_std, +}; +  static const struct v4l2_subdev_ops sony_btf_mpx_ops = { -	.core = &sony_btf_mpx_core_ops,  	.tuner = &sony_btf_mpx_tuner_ops, +	.video = &sony_btf_mpx_video_ops,  };  /* --------------------------------------------------------------------------*/ diff --git a/drivers/media/i2c/sr030pc30.c b/drivers/media/i2c/sr030pc30.c index ae9432637fc..118f8ee8846 100644 --- a/drivers/media/i2c/sr030pc30.c +++ b/drivers/media/i2c/sr030pc30.c @@ -8,7 +8,7 @@   * and HeungJun Kim <riverful.kim@samsung.com>.   *   * Based on mt9v011 Micron Digital Image Sensor driver - * Copyright (c) 2009 Mauro Carvalho Chehab (mchehab@redhat.com) + * Copyright (c) 2009 Mauro Carvalho Chehab   *   * This program is free software; you can redistribute it and/or modify   * it under the terms of the GNU General Public License as published by diff --git a/drivers/media/i2c/tcm825x.c b/drivers/media/i2c/tcm825x.c deleted file mode 100644 index 9252529fc5d..00000000000 --- a/drivers/media/i2c/tcm825x.c +++ /dev/null @@ -1,937 +0,0 @@ -/* - * drivers/media/i2c/tcm825x.c - * - * TCM825X camera sensor driver. - * - * Copyright (C) 2007 Nokia Corporation. - * - * Contact: Sakari Ailus <sakari.ailus@nokia.com> - * - * Based on code from David Cohen <david.cohen@indt.org.br> - * - * This driver was based on ov9640 sensor driver from MontaVista - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#include <linux/i2c.h> -#include <linux/module.h> -#include <media/v4l2-int-device.h> - -#include "tcm825x.h" - -/* - * The sensor has two fps modes: the lower one just gives half the fps - * at the same xclk than the high one. - */ -#define MAX_FPS 30 -#define MIN_FPS 8 -#define MAX_HALF_FPS (MAX_FPS / 2) -#define HIGH_FPS_MODE_LOWER_LIMIT 14 -#define DEFAULT_FPS MAX_HALF_FPS - -struct tcm825x_sensor { -	const struct tcm825x_platform_data *platform_data; -	struct v4l2_int_device *v4l2_int_device; -	struct i2c_client *i2c_client; -	struct v4l2_pix_format pix; -	struct v4l2_fract timeperframe; -}; - -/* list of image formats supported by TCM825X sensor */ -static const struct v4l2_fmtdesc tcm825x_formats[] = { -	{ -		.description = "YUYV (YUV 4:2:2), packed", -		.pixelformat = V4L2_PIX_FMT_UYVY, -	}, { -		/* Note:  V4L2 defines RGB565 as: -		 * -		 *      Byte 0                    Byte 1 -		 *      g2 g1 g0 r4 r3 r2 r1 r0   b4 b3 b2 b1 b0 g5 g4 g3 -		 * -		 * We interpret RGB565 as: -		 * -		 *      Byte 0                    Byte 1 -		 *      g2 g1 g0 b4 b3 b2 b1 b0   r4 r3 r2 r1 r0 g5 g4 g3 -		 */ -		.description = "RGB565, le", -		.pixelformat = V4L2_PIX_FMT_RGB565, -	}, -}; - -#define TCM825X_NUM_CAPTURE_FORMATS	ARRAY_SIZE(tcm825x_formats) - -/* - * TCM825X register configuration for all combinations of pixel format and - * image size - */ -static const struct tcm825x_reg subqcif	=	{ 0x20, TCM825X_PICSIZ }; -static const struct tcm825x_reg qcif	=	{ 0x18, TCM825X_PICSIZ }; -static const struct tcm825x_reg cif	=	{ 0x14, TCM825X_PICSIZ }; -static const struct tcm825x_reg qqvga	=	{ 0x0c, TCM825X_PICSIZ }; -static const struct tcm825x_reg qvga	=	{ 0x04, TCM825X_PICSIZ }; -static const struct tcm825x_reg vga	=	{ 0x00, TCM825X_PICSIZ }; - -static const struct tcm825x_reg yuv422	=	{ 0x00, TCM825X_PICFMT }; -static const struct tcm825x_reg rgb565	=	{ 0x02, TCM825X_PICFMT }; - -/* Our own specific controls */ -#define V4L2_CID_ALC				V4L2_CID_PRIVATE_BASE -#define V4L2_CID_H_EDGE_EN			V4L2_CID_PRIVATE_BASE + 1 -#define V4L2_CID_V_EDGE_EN			V4L2_CID_PRIVATE_BASE + 2 -#define V4L2_CID_LENS				V4L2_CID_PRIVATE_BASE + 3 -#define V4L2_CID_MAX_EXPOSURE_TIME		V4L2_CID_PRIVATE_BASE + 4 -#define V4L2_CID_LAST_PRIV			V4L2_CID_MAX_EXPOSURE_TIME - -/*  Video controls  */ -static struct vcontrol { -	struct v4l2_queryctrl qc; -	u16 reg; -	u16 start_bit; -} video_control[] = { -	{ -		{ -			.id = V4L2_CID_GAIN, -			.type = V4L2_CTRL_TYPE_INTEGER, -			.name = "Gain", -			.minimum = 0, -			.maximum = 63, -			.step = 1, -		}, -		.reg = TCM825X_AG, -		.start_bit = 0, -	}, -	{ -		{ -			.id = V4L2_CID_RED_BALANCE, -			.type = V4L2_CTRL_TYPE_INTEGER, -			.name = "Red Balance", -			.minimum = 0, -			.maximum = 255, -			.step = 1, -		}, -		.reg = TCM825X_MRG, -		.start_bit = 0, -	}, -	{ -		{ -			.id = V4L2_CID_BLUE_BALANCE, -			.type = V4L2_CTRL_TYPE_INTEGER, -			.name = "Blue Balance", -			.minimum = 0, -			.maximum = 255, -			.step = 1, -		}, -		.reg = TCM825X_MBG, -		.start_bit = 0, -	}, -	{ -		{ -			.id = V4L2_CID_AUTO_WHITE_BALANCE, -			.type = V4L2_CTRL_TYPE_BOOLEAN, -			.name = "Auto White Balance", -			.minimum = 0, -			.maximum = 1, -			.step = 0, -		}, -		.reg = TCM825X_AWBSW, -		.start_bit = 7, -	}, -	{ -		{ -			.id = V4L2_CID_EXPOSURE, -			.type = V4L2_CTRL_TYPE_INTEGER, -			.name = "Exposure Time", -			.minimum = 0, -			.maximum = 0x1fff, -			.step = 1, -		}, -		.reg = TCM825X_ESRSPD_U, -		.start_bit = 0, -	}, -	{ -		{ -			.id = V4L2_CID_HFLIP, -			.type = V4L2_CTRL_TYPE_BOOLEAN, -			.name = "Mirror Image", -			.minimum = 0, -			.maximum = 1, -			.step = 0, -		}, -		.reg = TCM825X_H_INV, -		.start_bit = 6, -	}, -	{ -		{ -			.id = V4L2_CID_VFLIP, -			.type = V4L2_CTRL_TYPE_BOOLEAN, -			.name = "Vertical Flip", -			.minimum = 0, -			.maximum = 1, -			.step = 0, -		}, -		.reg = TCM825X_V_INV, -		.start_bit = 7, -	}, -	/* Private controls */ -	{ -		{ -			.id = V4L2_CID_ALC, -			.type = V4L2_CTRL_TYPE_BOOLEAN, -			.name = "Auto Luminance Control", -			.minimum = 0, -			.maximum = 1, -			.step = 0, -		}, -		.reg = TCM825X_ALCSW, -		.start_bit = 7, -	}, -	{ -		{ -			.id = V4L2_CID_H_EDGE_EN, -			.type = V4L2_CTRL_TYPE_INTEGER, -			.name = "Horizontal Edge Enhancement", -			.minimum = 0, -			.maximum = 0xff, -			.step = 1, -		}, -		.reg = TCM825X_HDTG, -		.start_bit = 0, -	}, -	{ -		{ -			.id = V4L2_CID_V_EDGE_EN, -			.type = V4L2_CTRL_TYPE_INTEGER, -			.name = "Vertical Edge Enhancement", -			.minimum = 0, -			.maximum = 0xff, -			.step = 1, -		}, -		.reg = TCM825X_VDTG, -		.start_bit = 0, -	}, -	{ -		{ -			.id = V4L2_CID_LENS, -			.type = V4L2_CTRL_TYPE_INTEGER, -			.name = "Lens Shading Compensation", -			.minimum = 0, -			.maximum = 0x3f, -			.step = 1, -		}, -		.reg = TCM825X_LENS, -		.start_bit = 0, -	}, -	{ -		{ -			.id = V4L2_CID_MAX_EXPOSURE_TIME, -			.type = V4L2_CTRL_TYPE_INTEGER, -			.name = "Maximum Exposure Time", -			.minimum = 0, -			.maximum = 0x3, -			.step = 1, -		}, -		.reg = TCM825X_ESRLIM, -		.start_bit = 5, -	}, -}; - - -static const struct tcm825x_reg *tcm825x_siz_reg[NUM_IMAGE_SIZES] = -{ &subqcif, &qqvga, &qcif, &qvga, &cif, &vga }; - -static const struct tcm825x_reg *tcm825x_fmt_reg[NUM_PIXEL_FORMATS] = -{ &yuv422, &rgb565 }; - -/* - * Read a value from a register in an TCM825X sensor device.  The value is - * returned in 'val'. - * Returns zero if successful, or non-zero otherwise. - */ -static int tcm825x_read_reg(struct i2c_client *client, int reg) -{ -	int err; -	struct i2c_msg msg[2]; -	u8 reg_buf, data_buf = 0; - -	if (!client->adapter) -		return -ENODEV; - -	msg[0].addr = client->addr; -	msg[0].flags = 0; -	msg[0].len = 1; -	msg[0].buf = ®_buf; -	msg[1].addr = client->addr; -	msg[1].flags = I2C_M_RD; -	msg[1].len = 1; -	msg[1].buf = &data_buf; - -	reg_buf = reg; - -	err = i2c_transfer(client->adapter, msg, 2); -	if (err < 0) -		return err; -	return data_buf; -} - -/* - * Write a value to a register in an TCM825X sensor device. - * Returns zero if successful, or non-zero otherwise. - */ -static int tcm825x_write_reg(struct i2c_client *client, u8 reg, u8 val) -{ -	int err; -	struct i2c_msg msg[1]; -	unsigned char data[2]; - -	if (!client->adapter) -		return -ENODEV; - -	msg->addr = client->addr; -	msg->flags = 0; -	msg->len = 2; -	msg->buf = data; -	data[0] = reg; -	data[1] = val; -	err = i2c_transfer(client->adapter, msg, 1); -	if (err >= 0) -		return 0; -	return err; -} - -static int __tcm825x_write_reg_mask(struct i2c_client *client, -				    u8 reg, u8 val, u8 mask) -{ -	int rc; - -	/* need to do read - modify - write */ -	rc = tcm825x_read_reg(client, reg); -	if (rc < 0) -		return rc; - -	rc &= (~mask);	/* Clear the masked bits */ -	val &= mask;	/* Enforce mask on value */ -	val |= rc; - -	/* write the new value to the register */ -	rc = tcm825x_write_reg(client, reg, val); -	if (rc) -		return rc; - -	return 0; -} - -#define tcm825x_write_reg_mask(client, regmask, val)			\ -	__tcm825x_write_reg_mask(client, TCM825X_ADDR((regmask)), val,	\ -				 TCM825X_MASK((regmask))) - - -/* - * Initialize a list of TCM825X registers. - * The list of registers is terminated by the pair of values - * { TCM825X_REG_TERM, TCM825X_VAL_TERM }. - * Returns zero if successful, or non-zero otherwise. - */ -static int tcm825x_write_default_regs(struct i2c_client *client, -				      const struct tcm825x_reg *reglist) -{ -	int err; -	const struct tcm825x_reg *next = reglist; - -	while (!((next->reg == TCM825X_REG_TERM) -		 && (next->val == TCM825X_VAL_TERM))) { -		err = tcm825x_write_reg(client, next->reg, next->val); -		if (err) { -			dev_err(&client->dev, "register writing failed\n"); -			return err; -		} -		next++; -	} - -	return 0; -} - -static struct vcontrol *find_vctrl(int id) -{ -	int i; - -	if (id < V4L2_CID_BASE) -		return NULL; - -	for (i = 0; i < ARRAY_SIZE(video_control); i++) -		if (video_control[i].qc.id == id) -			return &video_control[i]; - -	return NULL; -} - -/* - * Find the best match for a requested image capture size.  The best match - * is chosen as the nearest match that has the same number or fewer pixels - * as the requested size, or the smallest image size if the requested size - * has fewer pixels than the smallest image. - */ -static enum image_size tcm825x_find_size(struct v4l2_int_device *s, -					 unsigned int width, -					 unsigned int height) -{ -	enum image_size isize; -	unsigned long pixels = width * height; -	struct tcm825x_sensor *sensor = s->priv; - -	for (isize = subQCIF; isize < VGA; isize++) { -		if (tcm825x_sizes[isize + 1].height -		    * tcm825x_sizes[isize + 1].width > pixels) { -			dev_dbg(&sensor->i2c_client->dev, "size %d\n", isize); - -			return isize; -		} -	} - -	dev_dbg(&sensor->i2c_client->dev, "format default VGA\n"); - -	return VGA; -} - -/* - * Configure the TCM825X for current image size, pixel format, and - * frame period. fper is the frame period (in seconds) expressed as a - * fraction. Returns zero if successful, or non-zero otherwise. The - * actual frame period is returned in fper. - */ -static int tcm825x_configure(struct v4l2_int_device *s) -{ -	struct tcm825x_sensor *sensor = s->priv; -	struct v4l2_pix_format *pix = &sensor->pix; -	enum image_size isize = tcm825x_find_size(s, pix->width, pix->height); -	struct v4l2_fract *fper = &sensor->timeperframe; -	enum pixel_format pfmt; -	int err; -	u32 tgt_fps; -	u8 val; - -	/* common register initialization */ -	err = tcm825x_write_default_regs( -		sensor->i2c_client, sensor->platform_data->default_regs()); -	if (err) -		return err; - -	/* configure image size */ -	val = tcm825x_siz_reg[isize]->val; -	dev_dbg(&sensor->i2c_client->dev, -		"configuring image size %d\n", isize); -	err = tcm825x_write_reg_mask(sensor->i2c_client, -				     tcm825x_siz_reg[isize]->reg, val); -	if (err) -		return err; - -	/* configure pixel format */ -	switch (pix->pixelformat) { -	default: -	case V4L2_PIX_FMT_RGB565: -		pfmt = RGB565; -		break; -	case V4L2_PIX_FMT_UYVY: -		pfmt = YUV422; -		break; -	} - -	dev_dbg(&sensor->i2c_client->dev, -		"configuring pixel format %d\n", pfmt); -	val = tcm825x_fmt_reg[pfmt]->val; - -	err = tcm825x_write_reg_mask(sensor->i2c_client, -				     tcm825x_fmt_reg[pfmt]->reg, val); -	if (err) -		return err; - -	/* -	 * For frame rate < 15, the FPS reg (addr 0x02, bit 7) must be -	 * set. Frame rate will be halved from the normal. -	 */ -	tgt_fps = fper->denominator / fper->numerator; -	if (tgt_fps <= HIGH_FPS_MODE_LOWER_LIMIT) { -		val = tcm825x_read_reg(sensor->i2c_client, 0x02); -		val |= 0x80; -		tcm825x_write_reg(sensor->i2c_client, 0x02, val); -	} - -	return 0; -} - -static int ioctl_queryctrl(struct v4l2_int_device *s, -				struct v4l2_queryctrl *qc) -{ -	struct vcontrol *control; - -	control = find_vctrl(qc->id); - -	if (control == NULL) -		return -EINVAL; - -	*qc = control->qc; - -	return 0; -} - -static int ioctl_g_ctrl(struct v4l2_int_device *s, -			     struct v4l2_control *vc) -{ -	struct tcm825x_sensor *sensor = s->priv; -	struct i2c_client *client = sensor->i2c_client; -	int val, r; -	struct vcontrol *lvc; - -	/* exposure time is special, spread across 2 registers */ -	if (vc->id == V4L2_CID_EXPOSURE) { -		int val_lower, val_upper; - -		val_upper = tcm825x_read_reg(client, -					     TCM825X_ADDR(TCM825X_ESRSPD_U)); -		if (val_upper < 0) -			return val_upper; -		val_lower = tcm825x_read_reg(client, -					     TCM825X_ADDR(TCM825X_ESRSPD_L)); -		if (val_lower < 0) -			return val_lower; - -		vc->value = ((val_upper & 0x1f) << 8) | (val_lower); -		return 0; -	} - -	lvc = find_vctrl(vc->id); -	if (lvc == NULL) -		return -EINVAL; - -	r = tcm825x_read_reg(client, TCM825X_ADDR(lvc->reg)); -	if (r < 0) -		return r; -	val = r & TCM825X_MASK(lvc->reg); -	val >>= lvc->start_bit; - -	if (val < 0) -		return val; - -	if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP) -		val ^= sensor->platform_data->is_upside_down(); - -	vc->value = val; -	return 0; -} - -static int ioctl_s_ctrl(struct v4l2_int_device *s, -			     struct v4l2_control *vc) -{ -	struct tcm825x_sensor *sensor = s->priv; -	struct i2c_client *client = sensor->i2c_client; -	struct vcontrol *lvc; -	int val = vc->value; - -	/* exposure time is special, spread across 2 registers */ -	if (vc->id == V4L2_CID_EXPOSURE) { -		int val_lower, val_upper; -		val_lower = val & TCM825X_MASK(TCM825X_ESRSPD_L); -		val_upper = (val >> 8) & TCM825X_MASK(TCM825X_ESRSPD_U); - -		if (tcm825x_write_reg_mask(client, -					   TCM825X_ESRSPD_U, val_upper)) -			return -EIO; - -		if (tcm825x_write_reg_mask(client, -					   TCM825X_ESRSPD_L, val_lower)) -			return -EIO; - -		return 0; -	} - -	lvc = find_vctrl(vc->id); -	if (lvc == NULL) -		return -EINVAL; - -	if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP) -		val ^= sensor->platform_data->is_upside_down(); - -	val = val << lvc->start_bit; -	if (tcm825x_write_reg_mask(client, lvc->reg, val)) -		return -EIO; - -	return 0; -} - -static int ioctl_enum_fmt_cap(struct v4l2_int_device *s, -				   struct v4l2_fmtdesc *fmt) -{ -	int index = fmt->index; - -	switch (fmt->type) { -	case V4L2_BUF_TYPE_VIDEO_CAPTURE: -		if (index >= TCM825X_NUM_CAPTURE_FORMATS) -			return -EINVAL; -		break; - -	default: -		return -EINVAL; -	} - -	fmt->flags = tcm825x_formats[index].flags; -	strlcpy(fmt->description, tcm825x_formats[index].description, -		sizeof(fmt->description)); -	fmt->pixelformat = tcm825x_formats[index].pixelformat; - -	return 0; -} - -static int ioctl_try_fmt_cap(struct v4l2_int_device *s, -			     struct v4l2_format *f) -{ -	struct tcm825x_sensor *sensor = s->priv; -	enum image_size isize; -	int ifmt; -	struct v4l2_pix_format *pix = &f->fmt.pix; - -	isize = tcm825x_find_size(s, pix->width, pix->height); -	dev_dbg(&sensor->i2c_client->dev, "isize = %d num_capture = %lu\n", -		isize, (unsigned long)TCM825X_NUM_CAPTURE_FORMATS); - -	pix->width = tcm825x_sizes[isize].width; -	pix->height = tcm825x_sizes[isize].height; - -	for (ifmt = 0; ifmt < TCM825X_NUM_CAPTURE_FORMATS; ifmt++) -		if (pix->pixelformat == tcm825x_formats[ifmt].pixelformat) -			break; - -	if (ifmt == TCM825X_NUM_CAPTURE_FORMATS) -		ifmt = 0;	/* Default = YUV 4:2:2 */ - -	pix->pixelformat = tcm825x_formats[ifmt].pixelformat; -	pix->field = V4L2_FIELD_NONE; -	pix->bytesperline = pix->width * TCM825X_BYTES_PER_PIXEL; -	pix->sizeimage = pix->bytesperline * pix->height; -	pix->priv = 0; -	dev_dbg(&sensor->i2c_client->dev, "format = 0x%08x\n", -		pix->pixelformat); - -	switch (pix->pixelformat) { -	case V4L2_PIX_FMT_UYVY: -	default: -		pix->colorspace = V4L2_COLORSPACE_JPEG; -		break; -	case V4L2_PIX_FMT_RGB565: -		pix->colorspace = V4L2_COLORSPACE_SRGB; -		break; -	} - -	return 0; -} - -static int ioctl_s_fmt_cap(struct v4l2_int_device *s, -				struct v4l2_format *f) -{ -	struct tcm825x_sensor *sensor = s->priv; -	struct v4l2_pix_format *pix = &f->fmt.pix; -	int rval; - -	rval = ioctl_try_fmt_cap(s, f); -	if (rval) -		return rval; - -	rval = tcm825x_configure(s); - -	sensor->pix = *pix; - -	return rval; -} - -static int ioctl_g_fmt_cap(struct v4l2_int_device *s, -				struct v4l2_format *f) -{ -	struct tcm825x_sensor *sensor = s->priv; - -	f->fmt.pix = sensor->pix; - -	return 0; -} - -static int ioctl_g_parm(struct v4l2_int_device *s, -			     struct v4l2_streamparm *a) -{ -	struct tcm825x_sensor *sensor = s->priv; -	struct v4l2_captureparm *cparm = &a->parm.capture; - -	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) -		return -EINVAL; - -	memset(a, 0, sizeof(*a)); -	a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - -	cparm->capability = V4L2_CAP_TIMEPERFRAME; -	cparm->timeperframe = sensor->timeperframe; - -	return 0; -} - -static int ioctl_s_parm(struct v4l2_int_device *s, -			     struct v4l2_streamparm *a) -{ -	struct tcm825x_sensor *sensor = s->priv; -	struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe; -	u32 tgt_fps;	/* target frames per secound */ -	int rval; - -	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) -		return -EINVAL; - -	if ((timeperframe->numerator == 0) -	    || (timeperframe->denominator == 0)) { -		timeperframe->denominator = DEFAULT_FPS; -		timeperframe->numerator = 1; -	} - -	tgt_fps = timeperframe->denominator / timeperframe->numerator; - -	if (tgt_fps > MAX_FPS) { -		timeperframe->denominator = MAX_FPS; -		timeperframe->numerator = 1; -	} else if (tgt_fps < MIN_FPS) { -		timeperframe->denominator = MIN_FPS; -		timeperframe->numerator = 1; -	} - -	sensor->timeperframe = *timeperframe; - -	rval = tcm825x_configure(s); - -	return rval; -} - -static int ioctl_s_power(struct v4l2_int_device *s, int on) -{ -	struct tcm825x_sensor *sensor = s->priv; - -	return sensor->platform_data->power_set(on); -} - -/* - * Given the image capture format in pix, the nominal frame period in - * timeperframe, calculate the required xclk frequency. - * - * TCM825X input frequency characteristics are: - *     Minimum 11.9 MHz, Typical 24.57 MHz and maximum 25/27 MHz - */ - -static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) -{ -	struct tcm825x_sensor *sensor = s->priv; -	struct v4l2_fract *timeperframe = &sensor->timeperframe; -	u32 tgt_xclk;	/* target xclk */ -	u32 tgt_fps;	/* target frames per secound */ -	int rval; - -	rval = sensor->platform_data->ifparm(p); -	if (rval) -		return rval; - -	tgt_fps = timeperframe->denominator / timeperframe->numerator; - -	tgt_xclk = (tgt_fps <= HIGH_FPS_MODE_LOWER_LIMIT) ? -		(2457 * tgt_fps) / MAX_HALF_FPS : -		(2457 * tgt_fps) / MAX_FPS; -	tgt_xclk *= 10000; - -	tgt_xclk = min(tgt_xclk, (u32)TCM825X_XCLK_MAX); -	tgt_xclk = max(tgt_xclk, (u32)TCM825X_XCLK_MIN); - -	p->u.bt656.clock_curr = tgt_xclk; - -	return 0; -} - -static int ioctl_g_needs_reset(struct v4l2_int_device *s, void *buf) -{ -	struct tcm825x_sensor *sensor = s->priv; - -	return sensor->platform_data->needs_reset(s, buf, &sensor->pix); -} - -static int ioctl_reset(struct v4l2_int_device *s) -{ -	return -EBUSY; -} - -static int ioctl_init(struct v4l2_int_device *s) -{ -	return tcm825x_configure(s); -} - -static int ioctl_dev_exit(struct v4l2_int_device *s) -{ -	return 0; -} - -static int ioctl_dev_init(struct v4l2_int_device *s) -{ -	struct tcm825x_sensor *sensor = s->priv; -	int r; - -	r = tcm825x_read_reg(sensor->i2c_client, 0x01); -	if (r < 0) -		return r; -	if (r == 0) { -		dev_err(&sensor->i2c_client->dev, "device not detected\n"); -		return -EIO; -	} -	return 0; -} - -static struct v4l2_int_ioctl_desc tcm825x_ioctl_desc[] = { -	{ vidioc_int_dev_init_num, -	  (v4l2_int_ioctl_func *)ioctl_dev_init }, -	{ vidioc_int_dev_exit_num, -	  (v4l2_int_ioctl_func *)ioctl_dev_exit }, -	{ vidioc_int_s_power_num, -	  (v4l2_int_ioctl_func *)ioctl_s_power }, -	{ vidioc_int_g_ifparm_num, -	  (v4l2_int_ioctl_func *)ioctl_g_ifparm }, -	{ vidioc_int_g_needs_reset_num, -	  (v4l2_int_ioctl_func *)ioctl_g_needs_reset }, -	{ vidioc_int_reset_num, -	  (v4l2_int_ioctl_func *)ioctl_reset }, -	{ vidioc_int_init_num, -	  (v4l2_int_ioctl_func *)ioctl_init }, -	{ vidioc_int_enum_fmt_cap_num, -	  (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap }, -	{ vidioc_int_try_fmt_cap_num, -	  (v4l2_int_ioctl_func *)ioctl_try_fmt_cap }, -	{ vidioc_int_g_fmt_cap_num, -	  (v4l2_int_ioctl_func *)ioctl_g_fmt_cap }, -	{ vidioc_int_s_fmt_cap_num, -	  (v4l2_int_ioctl_func *)ioctl_s_fmt_cap }, -	{ vidioc_int_g_parm_num, -	  (v4l2_int_ioctl_func *)ioctl_g_parm }, -	{ vidioc_int_s_parm_num, -	  (v4l2_int_ioctl_func *)ioctl_s_parm }, -	{ vidioc_int_queryctrl_num, -	  (v4l2_int_ioctl_func *)ioctl_queryctrl }, -	{ vidioc_int_g_ctrl_num, -	  (v4l2_int_ioctl_func *)ioctl_g_ctrl }, -	{ vidioc_int_s_ctrl_num, -	  (v4l2_int_ioctl_func *)ioctl_s_ctrl }, -}; - -static struct v4l2_int_slave tcm825x_slave = { -	.ioctls = tcm825x_ioctl_desc, -	.num_ioctls = ARRAY_SIZE(tcm825x_ioctl_desc), -}; - -static struct tcm825x_sensor tcm825x; - -static struct v4l2_int_device tcm825x_int_device = { -	.module = THIS_MODULE, -	.name = TCM825X_NAME, -	.priv = &tcm825x, -	.type = v4l2_int_type_slave, -	.u = { -		.slave = &tcm825x_slave, -	}, -}; - -static int tcm825x_probe(struct i2c_client *client, -			 const struct i2c_device_id *did) -{ -	struct tcm825x_sensor *sensor = &tcm825x; - -	if (i2c_get_clientdata(client)) -		return -EBUSY; - -	sensor->platform_data = client->dev.platform_data; - -	if (sensor->platform_data == NULL -	    || !sensor->platform_data->is_okay()) -		return -ENODEV; - -	sensor->v4l2_int_device = &tcm825x_int_device; - -	sensor->i2c_client = client; -	i2c_set_clientdata(client, sensor); - -	/* Make the default capture format QVGA RGB565 */ -	sensor->pix.width = tcm825x_sizes[QVGA].width; -	sensor->pix.height = tcm825x_sizes[QVGA].height; -	sensor->pix.pixelformat = V4L2_PIX_FMT_RGB565; - -	return v4l2_int_device_register(sensor->v4l2_int_device); -} - -static int tcm825x_remove(struct i2c_client *client) -{ -	struct tcm825x_sensor *sensor = i2c_get_clientdata(client); - -	if (!client->adapter) -		return -ENODEV;	/* our client isn't attached */ - -	v4l2_int_device_unregister(sensor->v4l2_int_device); - -	return 0; -} - -static const struct i2c_device_id tcm825x_id[] = { -	{ "tcm825x", 0 }, -	{ } -}; -MODULE_DEVICE_TABLE(i2c, tcm825x_id); - -static struct i2c_driver tcm825x_i2c_driver = { -	.driver	= { -		.name = TCM825X_NAME, -	}, -	.probe	= tcm825x_probe, -	.remove	= tcm825x_remove, -	.id_table = tcm825x_id, -}; - -static struct tcm825x_sensor tcm825x = { -	.timeperframe = { -		.numerator   = 1, -		.denominator = DEFAULT_FPS, -	}, -}; - -static int __init tcm825x_init(void) -{ -	int rval; - -	rval = i2c_add_driver(&tcm825x_i2c_driver); -	if (rval) -		printk(KERN_INFO "%s: failed registering " TCM825X_NAME "\n", -		       __func__); - -	return rval; -} - -static void __exit tcm825x_exit(void) -{ -	i2c_del_driver(&tcm825x_i2c_driver); -} - -/* - * FIXME: Menelaus isn't ready (?) at module_init stage, so use - * late_initcall for now. - */ -late_initcall(tcm825x_init); -module_exit(tcm825x_exit); - -MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>"); -MODULE_DESCRIPTION("TCM825x camera sensor driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/tcm825x.h b/drivers/media/i2c/tcm825x.h deleted file mode 100644 index 8ebab953963..00000000000 --- a/drivers/media/i2c/tcm825x.h +++ /dev/null @@ -1,200 +0,0 @@ -/* - * drivers/media/i2c/tcm825x.h - * - * Register definitions for the TCM825X CameraChip. - * - * Author: David Cohen (david.cohen@indt.org.br) - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - * - * This file was based on ov9640.h from MontaVista - */ - -#ifndef TCM825X_H -#define TCM825X_H - -#include <linux/videodev2.h> - -#include <media/v4l2-int-device.h> - -#define TCM825X_NAME "tcm825x" - -#define TCM825X_MASK(x)  x & 0x00ff -#define TCM825X_ADDR(x) (x & 0xff00) >> 8 - -/* The TCM825X I2C sensor chip has a fixed slave address of 0x3d. */ -#define TCM825X_I2C_ADDR	0x3d - -/* - * define register offsets for the TCM825X sensor chip - * OFFSET(8 bits) + MASK(8 bits) - * MASK bit 4 and 3 are used when the register uses more than one address - */ -#define TCM825X_FPS		0x0280 -#define TCM825X_ACF		0x0240 -#define TCM825X_DOUTBUF		0x020C -#define TCM825X_DCLKP		0x0202 -#define TCM825X_ACFDET		0x0201 -#define TCM825X_DOUTSW		0x0380 -#define TCM825X_DATAHZ		0x0340 -#define TCM825X_PICSIZ		0x033c -#define TCM825X_PICFMT		0x0302 -#define TCM825X_V_INV		0x0480 -#define TCM825X_H_INV		0x0440 -#define TCM825X_ESRLSW		0x0430 -#define TCM825X_V_LENGTH	0x040F -#define TCM825X_ALCSW		0x0580 -#define TCM825X_ESRLIM		0x0560 -#define TCM825X_ESRSPD_U        0x051F -#define TCM825X_ESRSPD_L        0x06FF -#define TCM825X_AG		0x07FF -#define TCM825X_ESRSPD2         0x06FF -#define TCM825X_ALCMODE         0x0830 -#define TCM825X_ALCH            0x080F -#define TCM825X_ALCL            0x09FF -#define TCM825X_AWBSW           0x0A80 -#define TCM825X_MRG             0x0BFF -#define TCM825X_MBG             0x0CFF -#define TCM825X_GAMSW           0x0D80 -#define TCM825X_HDTG            0x0EFF -#define TCM825X_VDTG            0x0FFF -#define TCM825X_HDTCORE         0x10F0 -#define TCM825X_VDTCORE         0x100F -#define TCM825X_CONT            0x11FF -#define TCM825X_BRIGHT          0x12FF -#define TCM825X_VHUE            0x137F -#define TCM825X_UHUE            0x147F -#define TCM825X_VGAIN           0x153F -#define TCM825X_UGAIN           0x163F -#define TCM825X_UVCORE          0x170F -#define TCM825X_SATU            0x187F -#define TCM825X_MHMODE          0x1980 -#define TCM825X_MHLPFSEL        0x1940 -#define TCM825X_YMODE           0x1930 -#define TCM825X_MIXHG           0x1907 -#define TCM825X_LENS            0x1A3F -#define TCM825X_AGLIM           0x1BE0 -#define TCM825X_LENSRPOL        0x1B10 -#define TCM825X_LENSRGAIN       0x1B0F -#define TCM825X_ES100S          0x1CFF -#define TCM825X_ES120S          0x1DFF -#define TCM825X_DMASK           0x1EC0 -#define TCM825X_CODESW          0x1E20 -#define TCM825X_CODESEL         0x1E10 -#define TCM825X_TESPIC          0x1E04 -#define TCM825X_PICSEL          0x1E03 -#define TCM825X_HNUM            0x20FF -#define TCM825X_VOUTPH          0x287F -#define TCM825X_ESROUT          0x327F -#define TCM825X_ESROUT2         0x33FF -#define TCM825X_AGOUT           0x34FF -#define TCM825X_DGOUT           0x353F -#define TCM825X_AGSLOW1         0x39C0 -#define TCM825X_FLLSMODE        0x3930 -#define TCM825X_FLLSLIM         0x390F -#define TCM825X_DETSEL          0x3AF0 -#define TCM825X_ACDETNC         0x3A0F -#define TCM825X_AGSLOW2         0x3BC0 -#define TCM825X_DG              0x3B3F -#define TCM825X_REJHLEV         0x3CFF -#define TCM825X_ALCLOCK         0x3D80 -#define TCM825X_FPSLNKSW        0x3D40 -#define TCM825X_ALCSPD          0x3D30 -#define TCM825X_REJH            0x3D03 -#define TCM825X_SHESRSW         0x3E80 -#define TCM825X_ESLIMSEL        0x3E40 -#define TCM825X_SHESRSPD        0x3E30 -#define TCM825X_ELSTEP          0x3E0C -#define TCM825X_ELSTART         0x3E03 -#define TCM825X_AGMIN           0x3FFF -#define TCM825X_PREGRG          0x423F -#define TCM825X_PREGBG          0x433F -#define TCM825X_PRERG           0x443F -#define TCM825X_PREBG           0x453F -#define TCM825X_MSKBR           0x477F -#define TCM825X_MSKGR           0x487F -#define TCM825X_MSKRB           0x497F -#define TCM825X_MSKGB           0x4A7F -#define TCM825X_MSKRG           0x4B7F -#define TCM825X_MSKBG           0x4C7F -#define TCM825X_HDTCSW          0x4D80 -#define TCM825X_VDTCSW          0x4D40 -#define TCM825X_DTCYL           0x4D3F -#define TCM825X_HDTPSW          0x4E80 -#define TCM825X_VDTPSW          0x4E40 -#define TCM825X_DTCGAIN         0x4E3F -#define TCM825X_DTLLIMSW        0x4F10 -#define TCM825X_DTLYLIM         0x4F0F -#define TCM825X_YLCUTLMSK       0x5080 -#define TCM825X_YLCUTL          0x503F -#define TCM825X_YLCUTHMSK       0x5180 -#define TCM825X_YLCUTH          0x513F -#define TCM825X_UVSKNC          0x527F -#define TCM825X_UVLJ            0x537F -#define TCM825X_WBGMIN          0x54FF -#define TCM825X_WBGMAX          0x55FF -#define TCM825X_WBSPDUP         0x5603 -#define TCM825X_ALLAREA         0x5820 -#define TCM825X_WBLOCK          0x5810 -#define TCM825X_WB2SP           0x580F -#define TCM825X_KIZUSW          0x5920 -#define TCM825X_PBRSW           0x5910 -#define TCM825X_ABCSW           0x5903 -#define TCM825X_PBDLV           0x5AFF -#define TCM825X_PBC1LV          0x5BFF - -#define TCM825X_NUM_REGS	(TCM825X_ADDR(TCM825X_PBC1LV) + 1) - -#define TCM825X_BYTES_PER_PIXEL 2 - -#define TCM825X_REG_TERM 0xff		/* terminating list entry for reg */ -#define TCM825X_VAL_TERM 0xff		/* terminating list entry for val */ - -/* define a structure for tcm825x register initialization values */ -struct tcm825x_reg { -	u8 val; -	u16 reg; -}; - -enum image_size { subQCIF = 0, QQVGA, QCIF, QVGA, CIF, VGA }; -enum pixel_format { YUV422 = 0, RGB565 }; -#define NUM_IMAGE_SIZES 6 -#define NUM_PIXEL_FORMATS 2 - -#define TCM825X_XCLK_MIN	11900000 -#define TCM825X_XCLK_MAX	25000000 - -struct capture_size { -	unsigned long width; -	unsigned long height; -}; - -struct tcm825x_platform_data { -	/* Is the sensor usable? Doesn't yet mean it's there, but you -	 * can try! */ -	int (*is_okay)(void); -	/* Set power state, zero is off, non-zero is on. */ -	int (*power_set)(int power); -	/* Default registers written after power-on or reset. */ -	const struct tcm825x_reg *(*default_regs)(void); -	int (*needs_reset)(struct v4l2_int_device *s, void *buf, -			   struct v4l2_pix_format *fmt); -	int (*ifparm)(struct v4l2_ifparm *p); -	int (*is_upside_down)(void); -}; - -/* Array of image sizes supported by TCM825X.  These must be ordered from - * smallest image size to largest. - */ -static const struct capture_size tcm825x_sizes[] = { -	{ 128,  96 }, /* subQCIF */ -	{ 160, 120 }, /* QQVGA */ -	{ 176, 144 }, /* QCIF */ -	{ 320, 240 }, /* QVGA */ -	{ 352, 288 }, /* CIF */ -	{ 640, 480 }, /* VGA */ -}; - -#endif /* ifndef TCM825X_H */ diff --git a/drivers/media/i2c/ths7303.c b/drivers/media/i2c/ths7303.c index 42276d93624..ed9ae887534 100644 --- a/drivers/media/i2c/ths7303.c +++ b/drivers/media/i2c/ths7303.c @@ -83,7 +83,8 @@ static int ths7303_write(struct v4l2_subdev *sd, u8 reg, u8 val)  }  /* following function is used to set ths7303 */ -int ths7303_setval(struct v4l2_subdev *sd, enum ths7303_filter_mode mode) +static int ths7303_setval(struct v4l2_subdev *sd, +			  enum ths7303_filter_mode mode)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd);  	struct ths7303_state *state = to_state(sd); diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c index a58a8f663ff..656d889c1c7 100644 --- a/drivers/media/i2c/ths8200.c +++ b/drivers/media/i2c/ths8200.c @@ -19,6 +19,7 @@  #include <linux/i2c.h>  #include <linux/module.h> +#include <linux/of.h>  #include <linux/v4l2-dv-timings.h>  #include <media/v4l2-dv-timings.h> @@ -46,14 +47,10 @@ struct ths8200_state {  static const struct v4l2_dv_timings_cap ths8200_timings_cap = {  	.type = V4L2_DV_BT_656_1120, -	.bt = { -		.max_width = 1920, -		.max_height = 1080, -		.min_pixelclock = 25000000, -		.max_pixelclock = 148500000, -		.standards = V4L2_DV_BT_STD_CEA861, -		.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE, -	}, +	/* keep this initialization for compatibility with GCC < 4.4.6 */ +	.reserved = { 0 }, +	V4L2_INIT_BT_TIMINGS(0, 1920, 0, 1080, 25000000, 148500000, +		V4L2_DV_BT_STD_CEA861, V4L2_DV_BT_CAP_PROGRESSIVE)  };  static inline struct ths8200_state *to_state(struct v4l2_subdev *sd) @@ -220,8 +217,8 @@ static void ths8200_core_init(struct v4l2_subdev *sd)  	/* Disable embedded syncs on the output by setting  	 * the amplitude to zero for all channels.  	 */ -	ths8200_write(sd, THS8200_DTG1_Y_SYNC_MSB, 0x2a); -	ths8200_write(sd, THS8200_DTG1_CBCR_SYNC_MSB, 0x2a); +	ths8200_write(sd, THS8200_DTG1_Y_SYNC_MSB, 0x00); +	ths8200_write(sd, THS8200_DTG1_CBCR_SYNC_MSB, 0x00);  }  static void ths8200_setup(struct v4l2_subdev *sd, struct v4l2_bt_timings *bt) @@ -321,15 +318,15 @@ static void ths8200_setup(struct v4l2_subdev *sd, struct v4l2_bt_timings *bt)  			     (htotal(bt) >> 8) & 0x1f);  	ths8200_write(sd, THS8200_DTG2_HLENGTH_HDLY_LSB, htotal(bt)); -	/* v sync width transmitted */ -	ths8200_write(sd, THS8200_DTG2_VLENGTH1_LSB, (bt->vsync) & 0xff); +	/* v sync width transmitted (must add 1 to get correct output) */ +	ths8200_write(sd, THS8200_DTG2_VLENGTH1_LSB, (bt->vsync + 1) & 0xff);  	ths8200_write_and_or(sd, THS8200_DTG2_VLENGTH1_MSB_VDLY1_MSB, 0x3f, -			     ((bt->vsync) >> 2) & 0xc0); +			     ((bt->vsync + 1) >> 2) & 0xc0); -	/* The pixel value v sync is asserted on */ +	/* The pixel value v sync is asserted on (must add 1 to get correct output) */  	ths8200_write_and_or(sd, THS8200_DTG2_VLENGTH1_MSB_VDLY1_MSB, 0xf8, -			     (vtotal(bt)>>8) & 0x7); -	ths8200_write(sd, THS8200_DTG2_VDLY1_LSB, vtotal(bt)); +			     ((vtotal(bt) + 1) >> 8) & 0x7); +	ths8200_write(sd, THS8200_DTG2_VDLY1_LSB, vtotal(bt) + 1);  	/* For progressive video vlength2 must be set to all 0 and vdly2 must  	 * be set to all 1. @@ -339,11 +336,11 @@ static void ths8200_setup(struct v4l2_subdev *sd, struct v4l2_bt_timings *bt)  	ths8200_write(sd, THS8200_DTG2_VDLY2_LSB, 0xff);  	/* Internal delay factors to synchronize the sync pulses and the data */ -	/* Experimental values delays (hor 4, ver 1) */ -	ths8200_write(sd, THS8200_DTG2_HS_IN_DLY_MSB, (htotal(bt)>>8) & 0x1f); -	ths8200_write(sd, THS8200_DTG2_HS_IN_DLY_LSB, (htotal(bt) - 4) & 0xff); +	/* Experimental values delays (hor 0, ver 0) */ +	ths8200_write(sd, THS8200_DTG2_HS_IN_DLY_MSB, 0); +	ths8200_write(sd, THS8200_DTG2_HS_IN_DLY_LSB, 0);  	ths8200_write(sd, THS8200_DTG2_VS_IN_DLY_MSB, 0); -	ths8200_write(sd, THS8200_DTG2_VS_IN_DLY_LSB, 1); +	ths8200_write(sd, THS8200_DTG2_VS_IN_DLY_LSB, 0);  	/* Polarity of received and transmitted sync signals */  	if (bt->polarities & V4L2_DV_HSYNC_POS_POL) { @@ -359,7 +356,7 @@ static void ths8200_setup(struct v4l2_subdev *sd, struct v4l2_bt_timings *bt)  	/* Timing of video input bus is derived from HS, VS, and FID dedicated  	 * inputs  	 */ -	ths8200_write(sd, THS8200_DTG2_CNTL, 0x47 | polarity); +	ths8200_write(sd, THS8200_DTG2_CNTL, 0x44 | polarity);  	/* leave reset */  	ths8200_s_stream(sd, true); @@ -413,6 +410,9 @@ static int ths8200_g_dv_timings(struct v4l2_subdev *sd,  static int ths8200_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, &ths8200_timings_cap,  			NULL, NULL);  } @@ -420,6 +420,9 @@ static int ths8200_enum_dv_timings(struct v4l2_subdev *sd,  static int ths8200_dv_timings_cap(struct v4l2_subdev *sd,  				  struct v4l2_dv_timings_cap *cap)  { +	if (cap->pad != 0) +		return -EINVAL; +  	*cap = ths8200_timings_cap;  	return 0;  } @@ -429,6 +432,9 @@ static const struct v4l2_subdev_video_ops ths8200_video_ops = {  	.s_stream = ths8200_s_stream,  	.s_dv_timings = ths8200_s_dv_timings,  	.g_dv_timings = ths8200_g_dv_timings, +}; + +static const struct v4l2_subdev_pad_ops ths8200_pad_ops = {  	.enum_dv_timings = ths8200_enum_dv_timings,  	.dv_timings_cap = ths8200_dv_timings_cap,  }; @@ -437,6 +443,7 @@ static const struct v4l2_subdev_video_ops ths8200_video_ops = {  static const struct v4l2_subdev_ops ths8200_ops = {  	.core  = &ths8200_core_ops,  	.video = &ths8200_video_ops, +	.pad = &ths8200_pad_ops,  };  static int ths8200_probe(struct i2c_client *client, diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c index d76c53a8f02..070c152da95 100644 --- a/drivers/media/i2c/tvaudio.c +++ b/drivers/media/i2c/tvaudio.c @@ -1862,7 +1862,6 @@ static const struct v4l2_subdev_core_ops tvaudio_core_ops = {  	.s_ctrl = v4l2_subdev_s_ctrl,  	.queryctrl = v4l2_subdev_queryctrl,  	.querymenu = v4l2_subdev_querymenu, -	.s_std = tvaudio_s_std,  };  static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = { @@ -1876,10 +1875,15 @@ static const struct v4l2_subdev_audio_ops tvaudio_audio_ops = {  	.s_routing = tvaudio_s_routing,  }; +static const struct v4l2_subdev_video_ops tvaudio_video_ops = { +	.s_std = tvaudio_s_std, +}; +  static const struct v4l2_subdev_ops tvaudio_ops = {  	.core = &tvaudio_core_ops,  	.tuner = &tvaudio_tuner_ops,  	.audio = &tvaudio_audio_ops, +	.video = &tvaudio_video_ops,  };  /* ----------------------------------------------------------------------- */ diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c index 91f3dd4cda1..b9dabc9f405 100644 --- a/drivers/media/i2c/tvp514x.c +++ b/drivers/media/i2c/tvp514x.c @@ -35,6 +35,8 @@  #include <linux/videodev2.h>  #include <linux/module.h>  #include <linux/v4l2-mediabus.h> +#include <linux/of.h> +#include <linux/of_graph.h>  #include <media/v4l2-async.h>  #include <media/v4l2-device.h> @@ -1008,10 +1010,10 @@ static const struct v4l2_subdev_core_ops tvp514x_core_ops = {  	.s_ctrl = v4l2_subdev_s_ctrl,  	.queryctrl = v4l2_subdev_queryctrl,  	.querymenu = v4l2_subdev_querymenu, -	.s_std = tvp514x_s_std,  };  static const struct v4l2_subdev_video_ops tvp514x_video_ops = { +	.s_std = tvp514x_s_std,  	.s_routing = tvp514x_s_routing,  	.querystd = tvp514x_querystd,  	.enum_mbus_fmt = tvp514x_enum_mbus_fmt, @@ -1067,7 +1069,7 @@ tvp514x_get_pdata(struct i2c_client *client)  	if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node)  		return client->dev.platform_data; -	endpoint = v4l2_of_get_next_endpoint(client->dev.of_node, NULL); +	endpoint = of_graph_get_next_endpoint(client->dev.of_node, NULL);  	if (!endpoint)  		return NULL; diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 89c0b13463b..a9121254e37 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -16,9 +16,9 @@  #include "tvp5150_reg.h" -#define TVP5150_H_MAX		720 -#define TVP5150_V_MAX_525_60	480 -#define TVP5150_V_MAX_OTHERS	576 +#define TVP5150_H_MAX		720U +#define TVP5150_V_MAX_525_60	480U +#define TVP5150_V_MAX_OTHERS	576U  #define TVP5150_MAX_CROP_LEFT	511  #define TVP5150_MAX_CROP_TOP	127  #define TVP5150_CROP_SHIFT	2 @@ -29,7 +29,7 @@ MODULE_LICENSE("GPL");  static int debug; -module_param(debug, int, 0); +module_param(debug, int, 0644);  MODULE_PARM_DESC(debug, "Debug level (0-2)");  struct tvp5150 { @@ -58,21 +58,17 @@ static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr)  	struct i2c_client *c = v4l2_get_subdevdata(sd);  	unsigned char buffer[1];  	int rc; - -	buffer[0] = addr; - -	rc = i2c_master_send(c, buffer, 1); -	if (rc < 0) { -		v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc); -		return rc; -	} - -	msleep(10); - -	rc = i2c_master_recv(c, buffer, 1); -	if (rc < 0) { -		v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc); -		return rc; +	struct i2c_msg msg[] = { +		{ .addr = c->addr, .flags = 0, +		  .buf = &addr, .len = 1 }, +		{ .addr = c->addr, .flags = I2C_M_RD, +		  .buf = buffer, .len = 1 } +	}; + +	rc = i2c_transfer(c->adapter, msg, 2); +	if (rc < 0 || rc != 2) { +		v4l2_err(sd, "i2c i/o error: rc == %d (should be 2)\n", rc); +		return rc < 0 ? rc : -EIO;  	}  	v4l2_dbg(2, debug, sd, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]); @@ -867,7 +863,7 @@ static int tvp5150_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)  	struct v4l2_rect rect = a->c;  	struct tvp5150 *decoder = to_tvp5150(sd);  	v4l2_std_id std; -	int hmax; +	unsigned int hmax;  	v4l2_dbg(1, debug, sd, "%s left=%d, top=%d, width=%d, height=%d\n",  		__func__, rect.left, rect.top, rect.width, rect.height); @@ -877,9 +873,9 @@ static int tvp5150_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)  	/* tvp5150 has some special limits */  	rect.left = clamp(rect.left, 0, TVP5150_MAX_CROP_LEFT); -	rect.width = clamp(rect.width, -			   TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left, -			   TVP5150_H_MAX - rect.left); +	rect.width = clamp_t(unsigned int, rect.width, +			     TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left, +			     TVP5150_H_MAX - rect.left);  	rect.top = clamp(rect.top, 0, TVP5150_MAX_CROP_TOP);  	/* Calculate height based on current standard */ @@ -893,9 +889,9 @@ static int tvp5150_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)  	else  		hmax = TVP5150_V_MAX_OTHERS; -	rect.height = clamp(rect.height, -			    hmax - TVP5150_MAX_CROP_TOP - rect.top, -			    hmax - rect.top); +	rect.height = clamp_t(unsigned int, rect.height, +			      hmax - TVP5150_MAX_CROP_TOP - rect.top, +			      hmax - rect.top);  	tvp5150_write(sd, TVP5150_VERT_BLANKING_START, rect.top);  	tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, @@ -917,7 +913,7 @@ static int tvp5150_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)  static int tvp5150_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)  { -	struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd); +	struct tvp5150 *decoder = to_tvp5150(sd);  	a->c	= decoder->rect;  	a->type	= V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -927,7 +923,7 @@ static int tvp5150_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)  static int tvp5150_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)  { -	struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd); +	struct tvp5150 *decoder = to_tvp5150(sd);  	v4l2_std_id std;  	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) @@ -1067,7 +1063,6 @@ static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = {  static const struct v4l2_subdev_core_ops tvp5150_core_ops = {  	.log_status = tvp5150_log_status, -	.s_std = tvp5150_s_std,  	.reset = tvp5150_reset,  #ifdef CONFIG_VIDEO_ADV_DEBUG  	.g_register = tvp5150_g_register, @@ -1080,6 +1075,7 @@ static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = {  };  static const struct v4l2_subdev_video_ops tvp5150_video_ops = { +	.s_std = tvp5150_s_std,  	.s_routing = tvp5150_s_routing,  	.enum_mbus_fmt = tvp5150_enum_mbus_fmt,  	.s_mbus_fmt = tvp5150_mbus_fmt, diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c index 24a08fa7e32..11f2387e1da 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -29,6 +29,8 @@  #include <linux/slab.h>  #include <linux/videodev2.h>  #include <linux/module.h> +#include <linux/of.h> +#include <linux/of_graph.h>  #include <linux/v4l2-dv-timings.h>  #include <media/tvp7002.h>  #include <media/v4l2-async.h> @@ -831,6 +833,9 @@ static int tvp7002_log_status(struct v4l2_subdev *sd)  static int tvp7002_enum_dv_timings(struct v4l2_subdev *sd,  		struct v4l2_enum_dv_timings *timings)  { +	if (timings->pad != 0) +		return -EINVAL; +  	/* Check requested format index is within range */  	if (timings->index >= NUM_TIMINGS)  		return -EINVAL; @@ -922,7 +927,6 @@ static const struct v4l2_subdev_core_ops tvp7002_core_ops = {  static const struct v4l2_subdev_video_ops tvp7002_video_ops = {  	.g_dv_timings = tvp7002_g_dv_timings,  	.s_dv_timings = tvp7002_s_dv_timings, -	.enum_dv_timings = tvp7002_enum_dv_timings,  	.query_dv_timings = tvp7002_query_dv_timings,  	.s_stream = tvp7002_s_stream,  	.g_mbus_fmt = tvp7002_mbus_fmt, @@ -936,6 +940,7 @@ static const struct v4l2_subdev_pad_ops tvp7002_pad_ops = {  	.enum_mbus_code = tvp7002_enum_mbus_code,  	.get_fmt = tvp7002_get_pad_format,  	.set_fmt = tvp7002_set_pad_format, +	.enum_dv_timings = tvp7002_enum_dv_timings,  };  /* V4L2 top level operation handlers */ @@ -956,7 +961,7 @@ tvp7002_get_pdata(struct i2c_client *client)  	if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node)  		return client->dev.platform_data; -	endpoint = v4l2_of_get_next_endpoint(client->dev.of_node, NULL); +	endpoint = of_graph_get_next_endpoint(client->dev.of_node, NULL);  	if (!endpoint)  		return NULL; diff --git a/drivers/media/i2c/tw2804.c b/drivers/media/i2c/tw2804.c index f58607df619..7347480c0b0 100644 --- a/drivers/media/i2c/tw2804.c +++ b/drivers/media/i2c/tw2804.c @@ -342,12 +342,12 @@ static const struct v4l2_ctrl_ops tw2804_ctrl_ops = {  };  static const struct v4l2_subdev_video_ops tw2804_video_ops = { +	.s_std = tw2804_s_std,  	.s_routing = tw2804_s_video_routing,  };  static const struct v4l2_subdev_core_ops tw2804_core_ops = {  	.log_status = tw2804_log_status, -	.s_std = tw2804_s_std,  };  static const struct v4l2_subdev_ops tw2804_ops = { diff --git a/drivers/media/i2c/tw9903.c b/drivers/media/i2c/tw9903.c index 285b759a5f7..12c7d211a4a 100644 --- a/drivers/media/i2c/tw9903.c +++ b/drivers/media/i2c/tw9903.c @@ -187,10 +187,10 @@ static const struct v4l2_ctrl_ops tw9903_ctrl_ops = {  static const struct v4l2_subdev_core_ops tw9903_core_ops = {  	.log_status = tw9903_log_status, -	.s_std = tw9903_s_std,  };  static const struct v4l2_subdev_video_ops tw9903_video_ops = { +	.s_std = tw9903_s_std,  	.s_routing = tw9903_s_video_routing,  }; diff --git a/drivers/media/i2c/tw9906.c b/drivers/media/i2c/tw9906.c index f6bef25bd9c..2672d89265f 100644 --- a/drivers/media/i2c/tw9906.c +++ b/drivers/media/i2c/tw9906.c @@ -157,10 +157,10 @@ static const struct v4l2_ctrl_ops tw9906_ctrl_ops = {  static const struct v4l2_subdev_core_ops tw9906_core_ops = {  	.log_status = tw9906_log_status, -	.s_std = tw9906_s_std,  };  static const struct v4l2_subdev_video_ops tw9906_video_ops = { +	.s_std = tw9906_s_std,  	.s_routing = tw9906_s_video_routing,  }; diff --git a/drivers/media/i2c/vp27smpx.c b/drivers/media/i2c/vp27smpx.c index 6a3a3ff7ee6..819ab6d1298 100644 --- a/drivers/media/i2c/vp27smpx.c +++ b/drivers/media/i2c/vp27smpx.c @@ -124,7 +124,6 @@ static int vp27smpx_log_status(struct v4l2_subdev *sd)  static const struct v4l2_subdev_core_ops vp27smpx_core_ops = {  	.log_status = vp27smpx_log_status, -	.s_std = vp27smpx_s_std,  };  static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = { @@ -133,9 +132,14 @@ static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = {  	.g_tuner = vp27smpx_g_tuner,  }; +static const struct v4l2_subdev_video_ops vp27smpx_video_ops = { +	.s_std = vp27smpx_s_std, +}; +  static const struct v4l2_subdev_ops vp27smpx_ops = {  	.core = &vp27smpx_core_ops,  	.tuner = &vp27smpx_tuner_ops, +	.video = &vp27smpx_video_ops,  };  /* ----------------------------------------------------------------------- */ diff --git a/drivers/media/i2c/vpx3220.c b/drivers/media/i2c/vpx3220.c index ece90df6a04..016e766e72b 100644 --- a/drivers/media/i2c/vpx3220.c +++ b/drivers/media/i2c/vpx3220.c @@ -457,10 +457,10 @@ static const struct v4l2_subdev_core_ops vpx3220_core_ops = {  	.s_ctrl = v4l2_subdev_s_ctrl,  	.queryctrl = v4l2_subdev_queryctrl,  	.querymenu = v4l2_subdev_querymenu, -	.s_std = vpx3220_s_std,  };  static const struct v4l2_subdev_video_ops vpx3220_video_ops = { +	.s_std = vpx3220_s_std,  	.s_routing = vpx3220_s_routing,  	.s_stream = vpx3220_s_stream,  	.querystd = vpx3220_querystd, diff --git a/drivers/media/i2c/vs6624.c b/drivers/media/i2c/vs6624.c index 25bdd9312fe..23f4f65fccd 100644 --- a/drivers/media/i2c/vs6624.c +++ b/drivers/media/i2c/vs6624.c @@ -503,6 +503,7 @@ static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)  	return &container_of(ctrl->handler, struct vs6624, hdl)->sd;  } +#ifdef CONFIG_VIDEO_ADV_DEBUG  static int vs6624_read(struct v4l2_subdev *sd, u16 index)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -515,6 +516,7 @@ static int vs6624_read(struct v4l2_subdev *sd, u16 index)  	return buf[0];  } +#endif  static int vs6624_write(struct v4l2_subdev *sd, u16 index,  				u8 value) diff --git a/drivers/media/i2c/wm8775.c b/drivers/media/i2c/wm8775.c index 3f584a7d078..bee7946faa7 100644 --- a/drivers/media/i2c/wm8775.c +++ b/drivers/media/i2c/wm8775.c @@ -130,12 +130,10 @@ static int wm8775_s_routing(struct v4l2_subdev *sd,  		return -EINVAL;  	}  	state->input = input; -	if (!v4l2_ctrl_g_ctrl(state->mute)) +	if (v4l2_ctrl_g_ctrl(state->mute))  		return 0;  	if (!v4l2_ctrl_g_ctrl(state->vol))  		return 0; -	if (!v4l2_ctrl_g_ctrl(state->bal)) -		return 0;  	wm8775_set_audio(sd, 1);  	return 0;  }  | 
