diff options
Diffstat (limited to 'drivers/media/i2c')
56 files changed, 2491 insertions, 505 deletions
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 7b771baa221..f981d50a2a8 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -112,6 +112,15 @@ config VIDEO_TLV320AIC23B  	  To compile this driver as a module, choose M here: the  	  module will be called tlv320aic23b. +config VIDEO_UDA1342 +	tristate "Philips UDA1342 audio codec" +	depends on VIDEO_V4L2 && I2C +	---help--- +	  Support for the Philips UDA1342 audio codec. + +	  To compile this driver as a module, choose M here: the +	  module will be called uda1342. +  config VIDEO_WM8775  	tristate "Wolfson Microelectronics WM8775 audio ADC with input mixer"  	depends on VIDEO_V4L2 && I2C @@ -133,7 +142,7 @@ config VIDEO_WM8739  	  module will be called wm8739.  config VIDEO_VP27SMPX -	tristate "Panasonic VP27s internal MPX" +	tristate "Panasonic VP27's internal MPX"  	depends on VIDEO_V4L2 && I2C  	---help---  	  Support for the internal MPX of the Panasonic VP27s tuner. @@ -141,6 +150,15 @@ config VIDEO_VP27SMPX  	  To compile this driver as a module, choose M here: the  	  module will be called vp27smpx. +config VIDEO_SONY_BTF_MPX +	tristate "Sony BTF's internal MPX" +	depends on VIDEO_V4L2 && I2C +	help +	  Support for the internal MPX of the Sony BTF-PG472Z tuner. + +	  To compile this driver as a module, choose M here: the +	  module will be called sony-btf-mpx. +  comment "RDS decoders"  config VIDEO_SAA6588 @@ -283,6 +301,35 @@ config VIDEO_TVP7002  	  To compile this driver as a module, choose M here: the  	  module will be called tvp7002. +config VIDEO_TW2804 +	tristate "Techwell TW2804 multiple video decoder" +	depends on VIDEO_V4L2 && I2C +	---help--- +	  Support for the Techwell tw2804 multiple video decoder. + +	  To compile this driver as a module, choose M here: the +	  module will be called tw2804. + +config VIDEO_TW9903 +	tristate "Techwell TW9903 video decoder" +	depends on VIDEO_V4L2 && I2C +	---help--- +	  Support for the Techwell tw9903 multi-standard video decoder +	  with high quality down scaler. + +	  To compile this driver as a module, choose M here: the +	  module will be called tw9903. + +config VIDEO_TW9906 +	tristate "Techwell TW9906 video decoder" +	depends on VIDEO_V4L2 && I2C +	---help--- +	  Support for the Techwell tw9906 enhanced multi-standard comb filter +	  video decoder with YCbCr input support. + +	  To compile this driver as a module, choose M here: the +	  module will be called tw9906. +  config VIDEO_VPX3220  	tristate "vpx3220a, vpx3216b & vpx3214c video decoders"  	depends on VIDEO_V4L2 && I2C @@ -386,6 +433,17 @@ config VIDEO_APTINA_PLL  config VIDEO_SMIAPP_PLL  	tristate +config VIDEO_OV7640 +	tristate "OmniVision OV7640 sensor support" +	depends on I2C && VIDEO_V4L2 +	depends on MEDIA_CAMERA_SUPPORT +	---help--- +	  This is a Video4Linux2 sensor-level driver for the OmniVision +	  OV7640 camera. + +	  To compile this driver as a module, choose M here: the +	  module will be called ov7640. +  config VIDEO_OV7670  	tristate "OmniVision OV7670 sensor support"  	depends on I2C && VIDEO_V4L2 @@ -501,8 +559,8 @@ config VIDEO_S5C73M3  	tristate "Samsung S5C73M3 sensor support"  	depends on I2C && SPI && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API  	---help--- -	This is a V4L2 sensor-level driver for Samsung S5C73M3 -	8 Mpixel camera. +	  This is a V4L2 sensor-level driver for Samsung S5C73M3 +	  8 Mpixel camera.  comment "Flash devices" @@ -550,10 +608,10 @@ config VIDEO_UPD64083  comment "Miscelaneous helper chips"  config VIDEO_THS7303 -	tristate "THS7303 Video Amplifier" -	depends on I2C +	tristate "THS7303/53 Video Amplifier" +	depends on VIDEO_V4L2 && I2C  	help -	  Support for TI THS7303 video amplifier +	  Support for TI THS7303/53 video amplifier  	  To compile this driver as a module, choose M here: the  	  module will be called ths7303. diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index cfefd30cc1b..720f42d9d9f 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -37,16 +37,22 @@ obj-$(CONFIG_VIDEO_THS7303) += ths7303.o  obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o  obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o  obj-$(CONFIG_VIDEO_TVP7002) += tvp7002.o +obj-$(CONFIG_VIDEO_TW2804) += tw2804.o +obj-$(CONFIG_VIDEO_TW9903) += tw9903.o +obj-$(CONFIG_VIDEO_TW9906) += tw9906.o  obj-$(CONFIG_VIDEO_CS5345) += cs5345.o  obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o  obj-$(CONFIG_VIDEO_M52790) += m52790.o  obj-$(CONFIG_VIDEO_TLV320AIC23B) += tlv320aic23b.o +obj-$(CONFIG_VIDEO_UDA1342) += uda1342.o  obj-$(CONFIG_VIDEO_WM8775) += wm8775.o  obj-$(CONFIG_VIDEO_WM8739) += wm8739.o  obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o +obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o  obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o  obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o -obj-$(CONFIG_VIDEO_OV7670) 	+= ov7670.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 diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index c2886b6a727..58344b6c3a5 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -354,7 +354,7 @@ static int ad9389b_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *  	return 0;  } -static int ad9389b_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int ad9389b_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/adp1653.c b/drivers/media/i2c/adp1653.c index df163800c8e..ef75abe5984 100644 --- a/drivers/media/i2c/adp1653.c +++ b/drivers/media/i2c/adp1653.c @@ -447,7 +447,7 @@ free_and_quit:  	return ret;  } -static int __exit adp1653_remove(struct i2c_client *client) +static int adp1653_remove(struct i2c_client *client)  {  	struct v4l2_subdev *subdev = i2c_get_clientdata(client);  	struct adp1653_flash *flash = to_adp1653_flash(subdev); @@ -476,7 +476,7 @@ static struct i2c_driver adp1653_i2c_driver = {  		.pm	= &adp1653_pm_ops,  	},  	.probe		= adp1653_probe, -	.remove		= __exit_p(adp1653_remove), +	.remove		= adp1653_remove,  	.id_table	= adp1653_id_table,  }; diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 34f39d3b3e3..afd561ab190 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -135,6 +135,10 @@ struct adv7180_state {  static v4l2_std_id adv7180_std_to_v4l2(u8 status1)  { +	/* in case V4L2_IN_ST_NO_SIGNAL */ +	if (!(status1 & ADV7180_STATUS1_IN_LOCK)) +		return V4L2_STD_UNKNOWN; +  	switch (status1 & ADV7180_STATUS1_AUTOD_MASK) {  	case ADV7180_STATUS1_AUTOD_NTSM_M_J:  		return V4L2_STD_NTSC; diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c index 6fed5b74e74..56a1fa4af0f 100644 --- a/drivers/media/i2c/adv7183.c +++ b/drivers/media/i2c/adv7183.c @@ -507,7 +507,7 @@ static int adv7183_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *  	return 0;  } -static int adv7183_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int adv7183_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index f47555b1000..31a63c9324f 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -699,7 +699,7 @@ static int adv7604_g_register(struct v4l2_subdev *sd,  }  static int adv7604_s_register(struct v4l2_subdev *sd, -					struct v4l2_dbg_register *reg) +					const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/ak881x.c b/drivers/media/i2c/ak881x.c index ba674656b10..fd47465e4f6 100644 --- a/drivers/media/i2c/ak881x.c +++ b/drivers/media/i2c/ak881x.c @@ -101,7 +101,7 @@ static int ak881x_g_register(struct v4l2_subdev *sd,  }  static int ak881x_s_register(struct v4l2_subdev *sd, -			     struct v4l2_dbg_register *reg) +			     const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/cs5345.c b/drivers/media/i2c/cs5345.c index c8581e26fa9..1d2f7c8512b 100644 --- a/drivers/media/i2c/cs5345.c +++ b/drivers/media/i2c/cs5345.c @@ -110,7 +110,7 @@ static int cs5345_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *r  	return 0;  } -static int cs5345_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int cs5345_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c index f4149eb4d7b..12fb9b2eb88 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.c +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -1671,7 +1671,7 @@ static int cx25840_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *  	return 0;  } -static int cx25840_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int cx25840_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -1835,7 +1835,7 @@ static int cx25840_s_audio_routing(struct v4l2_subdev *sd,  	return set_input(client, state->vid_input, input);  } -static int cx25840_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +static int cx25840_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequency *freq)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -1881,7 +1881,7 @@ static int cx25840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)  	return 0;  } -static int cx25840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +static int cx25840_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt)  {  	struct cx25840_state *state = to_state(sd);  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c index 08ae067b2b6..8e2f79cb045 100644 --- a/drivers/media/i2c/ir-kbd-i2c.c +++ b/drivers/media/i2c/ir-kbd-i2c.c @@ -230,7 +230,7 @@ static int get_key_avermedia_cardbus(struct IR_i2c *ir,  		return 0;  	dprintk(1, "read key 0x%02x/0x%02x\n", key, keygroup); -	if (keygroup < 2 || keygroup > 3) { +	if (keygroup < 2 || keygroup > 4) {  		/* Only a warning */  		dprintk(1, "warning: invalid key group 0x%02x for key 0x%02x\n",  								keygroup, key); @@ -239,6 +239,10 @@ static int get_key_avermedia_cardbus(struct IR_i2c *ir,  	*ir_key = key;  	*ir_raw = key; +	if (!strcmp(ir->ir_codes, RC_MAP_AVERMEDIA_M733A_RM_K6)) { +		*ir_key |= keygroup << 8; +		*ir_raw |= keygroup << 8; +	}  	return 1;  } @@ -332,6 +336,13 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)  		rc_type     = RC_BIT_OTHER;  		ir_codes    = RC_MAP_AVERMEDIA_CARDBUS;  		break; +	case 0x41: +		name        = "AVerMedia EM78P153"; +		ir->get_key = get_key_avermedia_cardbus; +		rc_type     = RC_BIT_OTHER; +		/* RM-KV remote, seems to be same as RM-K6 */ +		ir_codes    = RC_MAP_AVERMEDIA_M733A_RM_K6; +		break;  	case 0x71:  		name        = "Hauppauge/Zilog Z8";  		ir->get_key = get_key_haup_xvr; @@ -423,6 +434,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)  	 */  	rc->map_name       = ir->ir_codes;  	rc->allowed_protos = rc_type; +	rc->enabled_protocols = rc_type;  	if (!rc->driver_name)  		rc->driver_name = MODULE_NAME; diff --git a/drivers/media/i2c/m52790.c b/drivers/media/i2c/m52790.c index 0991576f4c8..39f50fd2b8d 100644 --- a/drivers/media/i2c/m52790.c +++ b/drivers/media/i2c/m52790.c @@ -96,7 +96,7 @@ static int m52790_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *r  	return 0;  } -static int m52790_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int m52790_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)  {  	struct m52790_state *state = to_state(sd);  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/m5mols/m5mols_core.c b/drivers/media/i2c/m5mols/m5mols_core.c index d4e7567b367..0b899cb6cda 100644 --- a/drivers/media/i2c/m5mols/m5mols_core.c +++ b/drivers/media/i2c/m5mols/m5mols_core.c @@ -724,7 +724,7 @@ static int m5mols_s_stream(struct v4l2_subdev *sd, int enable)  	if (enable) {  		if (is_code(code, M5MOLS_RESTYPE_MONITOR))  			ret = m5mols_start_monitor(info); -		if (is_code(code, M5MOLS_RESTYPE_CAPTURE)) +		else if (is_code(code, M5MOLS_RESTYPE_CAPTURE))  			ret = m5mols_start_capture(info);  		else  			ret = -EINVAL; diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c index 766305f69a2..54a9dd394f4 100644 --- a/drivers/media/i2c/msp3400-driver.c +++ b/drivers/media/i2c/msp3400-driver.c @@ -445,7 +445,7 @@ static int msp_s_radio(struct v4l2_subdev *sd)  	return 0;  } -static int msp_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +static int msp_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequency *freq)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -535,7 +535,7 @@ static int msp_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)  	return 0;  } -static int msp_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +static int msp_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt)  {  	struct msp_state *state = to_state(sd);  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/mt9m032.c b/drivers/media/i2c/mt9m032.c index f80c1d7ec88..8edb3d8f7b9 100644 --- a/drivers/media/i2c/mt9m032.c +++ b/drivers/media/i2c/mt9m032.c @@ -87,9 +87,27 @@  #define MT9M032_RESTART					0x0b  #define MT9M032_RESET					0x0d  #define MT9M032_PLL_CONFIG1				0x11 -#define		MT9M032_PLL_CONFIG1_OUTDIV_MASK		0x3f +#define		MT9M032_PLL_CONFIG1_PREDIV_MASK		0x3f  #define		MT9M032_PLL_CONFIG1_MUL_SHIFT		8  #define MT9M032_READ_MODE1				0x1e +#define		MT9M032_READ_MODE1_OUTPUT_BAD_FRAMES	(1 << 13) +#define		MT9M032_READ_MODE1_MAINTAIN_FRAME_RATE	(1 << 12) +#define		MT9M032_READ_MODE1_XOR_LINE_VALID	(1 << 11) +#define		MT9M032_READ_MODE1_CONT_LINE_VALID	(1 << 10) +#define		MT9M032_READ_MODE1_INVERT_TRIGGER	(1 << 9) +#define		MT9M032_READ_MODE1_SNAPSHOT		(1 << 8) +#define		MT9M032_READ_MODE1_GLOBAL_RESET		(1 << 7) +#define		MT9M032_READ_MODE1_BULB_EXPOSURE	(1 << 6) +#define		MT9M032_READ_MODE1_INVERT_STROBE	(1 << 5) +#define		MT9M032_READ_MODE1_STROBE_ENABLE	(1 << 4) +#define		MT9M032_READ_MODE1_STROBE_START_TRIG1	(0 << 2) +#define		MT9M032_READ_MODE1_STROBE_START_EXP	(1 << 2) +#define		MT9M032_READ_MODE1_STROBE_START_SHUTTER	(2 << 2) +#define		MT9M032_READ_MODE1_STROBE_START_TRIG2	(3 << 2) +#define		MT9M032_READ_MODE1_STROBE_END_TRIG1	(0 << 0) +#define		MT9M032_READ_MODE1_STROBE_END_EXP	(1 << 0) +#define		MT9M032_READ_MODE1_STROBE_END_SHUTTER	(2 << 0) +#define		MT9M032_READ_MODE1_STROBE_END_TRIG2	(3 << 0)  #define MT9M032_READ_MODE2				0x20  #define		MT9M032_READ_MODE2_VFLIP_SHIFT		15  #define		MT9M032_READ_MODE2_HFLIP_SHIFT		14 @@ -106,6 +124,8 @@  #define		MT9M032_GAIN_AMUL_SHIFT			6  #define		MT9M032_GAIN_ANALOG_MASK		0x3f  #define MT9M032_FORMATTER1				0x9e +#define		MT9M032_FORMATTER1_PLL_P1_6		(1 << 8) +#define		MT9M032_FORMATTER1_PARALLEL		(1 << 12)  #define MT9M032_FORMATTER2				0x9f  #define		MT9M032_FORMATTER2_DOUT_EN		0x1000  #define		MT9M032_FORMATTER2_PIXCLK_EN		0x2000 @@ -121,8 +141,6 @@  #define		MT9P031_PLL_CONTROL_PWROFF		0x0050  #define		MT9P031_PLL_CONTROL_PWRON		0x0051  #define		MT9P031_PLL_CONTROL_USEPLL		0x0052 -#define MT9P031_PLL_CONFIG2				0x11 -#define		MT9P031_PLL_CONFIG2_P1_DIV_MASK		0x1f  struct mt9m032 {  	struct v4l2_subdev subdev; @@ -255,13 +273,14 @@ static int mt9m032_setup_pll(struct mt9m032 *sensor)  		.n_max = 64,  		.m_min = 16,  		.m_max = 255, -		.p1_min = 1, -		.p1_max = 128, +		.p1_min = 6, +		.p1_max = 7,  	};  	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);  	struct mt9m032_platform_data *pdata = sensor->pdata;  	struct aptina_pll pll; +	u16 reg_val;  	int ret;  	pll.ext_clock = pdata->ext_clock; @@ -274,18 +293,21 @@ static int mt9m032_setup_pll(struct mt9m032 *sensor)  	sensor->pix_clock = pdata->pix_clock;  	ret = mt9m032_write(client, MT9M032_PLL_CONFIG1, -			    (pll.m << MT9M032_PLL_CONFIG1_MUL_SHIFT) -			    | (pll.p1 - 1)); -	if (!ret) -		ret = mt9m032_write(client, MT9P031_PLL_CONFIG2, pll.n - 1); +			    (pll.m << MT9M032_PLL_CONFIG1_MUL_SHIFT) | +			    ((pll.n - 1) & MT9M032_PLL_CONFIG1_PREDIV_MASK));  	if (!ret)  		ret = mt9m032_write(client, MT9P031_PLL_CONTROL,  				    MT9P031_PLL_CONTROL_PWRON |  				    MT9P031_PLL_CONTROL_USEPLL);  	if (!ret)		/* more reserved, Continuous, Master Mode */ -		ret = mt9m032_write(client, MT9M032_READ_MODE1, 0x8006); -	if (!ret)		/* Set 14-bit mode, select 7 divider */ -		ret = mt9m032_write(client, MT9M032_FORMATTER1, 0x111e); +		ret = mt9m032_write(client, MT9M032_READ_MODE1, 0x8000 | +				    MT9M032_READ_MODE1_STROBE_START_EXP | +				    MT9M032_READ_MODE1_STROBE_END_SHUTTER); +	if (!ret) { +		reg_val = (pll.p1 == 6 ? MT9M032_FORMATTER1_PLL_P1_6 : 0) +			| MT9M032_FORMATTER1_PARALLEL | 0x001e; /* 14-bit */ +		ret = mt9m032_write(client, MT9M032_FORMATTER1, reg_val); +	}  	return ret;  } @@ -548,7 +570,7 @@ static int mt9m032_g_register(struct v4l2_subdev *sd,  }  static int mt9m032_s_register(struct v4l2_subdev *sd, -			      struct v4l2_dbg_register *reg) +			      const struct v4l2_dbg_register *reg)  {  	struct mt9m032 *sensor = to_mt9m032(sd);  	struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index e32833262d3..28cf95b3728 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -12,6 +12,7 @@   * published by the Free Software Foundation.   */ +#include <linux/clk.h>  #include <linux/delay.h>  #include <linux/device.h>  #include <linux/gpio.h> @@ -19,6 +20,7 @@  #include <linux/i2c.h>  #include <linux/log2.h>  #include <linux/pm.h> +#include <linux/regulator/consumer.h>  #include <linux/slab.h>  #include <linux/videodev2.h> @@ -121,6 +123,11 @@ struct mt9p031 {  	struct mutex power_lock; /* lock to protect power_count */  	int power_count; +	struct clk *clk; +	struct regulator *vaa; +	struct regulator *vdd; +	struct regulator *vdd_io; +  	enum mt9p031_model model;  	struct aptina_pll pll;  	int reset; @@ -195,7 +202,7 @@ static int mt9p031_reset(struct mt9p031 *mt9p031)  					  0);  } -static int mt9p031_pll_setup(struct mt9p031 *mt9p031) +static int mt9p031_clk_setup(struct mt9p031 *mt9p031)  {  	static const struct aptina_pll_limits limits = {  		.ext_clock_min = 6000000, @@ -216,6 +223,12 @@ static int mt9p031_pll_setup(struct mt9p031 *mt9p031)  	struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);  	struct mt9p031_platform_data *pdata = mt9p031->pdata; +	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); +  	mt9p031->pll.ext_clock = pdata->ext_freq;  	mt9p031->pll.pix_clock = pdata->target_freq; @@ -264,10 +277,14 @@ static int mt9p031_power_on(struct mt9p031 *mt9p031)  		usleep_range(1000, 2000);  	} +	/* Bring up the supplies */ +	regulator_enable(mt9p031->vdd); +	regulator_enable(mt9p031->vdd_io); +	regulator_enable(mt9p031->vaa); +  	/* Emable clock */ -	if (mt9p031->pdata->set_xclk) -		mt9p031->pdata->set_xclk(&mt9p031->subdev, -					 mt9p031->pdata->ext_freq); +	if (mt9p031->clk) +		clk_prepare_enable(mt9p031->clk);  	/* Now RESET_BAR must be high */  	if (mt9p031->reset != -1) { @@ -285,8 +302,12 @@ static void mt9p031_power_off(struct mt9p031 *mt9p031)  		usleep_range(1000, 2000);  	} -	if (mt9p031->pdata->set_xclk) -		mt9p031->pdata->set_xclk(&mt9p031->subdev, 0); +	regulator_disable(mt9p031->vaa); +	regulator_disable(mt9p031->vdd_io); +	regulator_disable(mt9p031->vdd); + +	if (mt9p031->clk) +		clk_disable_unprepare(mt9p031->clk);  }  static int __mt9p031_set_power(struct mt9p031 *mt9p031, bool on) @@ -927,7 +948,7 @@ static int mt9p031_probe(struct i2c_client *client,  		return -EIO;  	} -	mt9p031 = kzalloc(sizeof(*mt9p031), GFP_KERNEL); +	mt9p031 = devm_kzalloc(&client->dev, sizeof(*mt9p031), GFP_KERNEL);  	if (mt9p031 == NULL)  		return -ENOMEM; @@ -937,6 +958,16 @@ static int mt9p031_probe(struct i2c_client *client,  	mt9p031->model = did->driver_data;  	mt9p031->reset = -1; +	mt9p031->vaa = devm_regulator_get(&client->dev, "vaa"); +	mt9p031->vdd = devm_regulator_get(&client->dev, "vdd"); +	mt9p031->vdd_io = devm_regulator_get(&client->dev, "vdd_io"); + +	if (IS_ERR(mt9p031->vaa) || IS_ERR(mt9p031->vdd) || +	    IS_ERR(mt9p031->vdd_io)) { +		dev_err(&client->dev, "Unable to get regulators\n"); +		return -ENODEV; +	} +  	v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 6);  	v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, @@ -1001,24 +1032,20 @@ static int mt9p031_probe(struct i2c_client *client,  	mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB;  	if (pdata->reset != -1) { -		ret = gpio_request_one(pdata->reset, GPIOF_OUT_INIT_LOW, -				       "mt9p031_rst"); +		ret = devm_gpio_request_one(&client->dev, pdata->reset, +					    GPIOF_OUT_INIT_LOW, "mt9p031_rst");  		if (ret < 0)  			goto done;  		mt9p031->reset = pdata->reset;  	} -	ret = mt9p031_pll_setup(mt9p031); +	ret = mt9p031_clk_setup(mt9p031);  done:  	if (ret < 0) { -		if (mt9p031->reset != -1) -			gpio_free(mt9p031->reset); -  		v4l2_ctrl_handler_free(&mt9p031->ctrls);  		media_entity_cleanup(&mt9p031->subdev.entity); -		kfree(mt9p031);  	}  	return ret; @@ -1032,9 +1059,6 @@ static int mt9p031_remove(struct i2c_client *client)  	v4l2_ctrl_handler_free(&mt9p031->ctrls);  	v4l2_device_unregister_subdev(subdev);  	media_entity_cleanup(&subdev->entity); -	if (mt9p031->reset != -1) -		gpio_free(mt9p031->reset); -	kfree(mt9p031);  	return 0;  } diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c index 73b7688cbeb..3f415fd12de 100644 --- a/drivers/media/i2c/mt9v011.c +++ b/drivers/media/i2c/mt9v011.c @@ -421,7 +421,7 @@ static int mt9v011_g_register(struct v4l2_subdev *sd,  }  static int mt9v011_s_register(struct v4l2_subdev *sd, -			      struct v4l2_dbg_register *reg) +			      const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/ov7640.c b/drivers/media/i2c/ov7640.c new file mode 100644 index 00000000000..b0cc927e8b1 --- /dev/null +++ b/drivers/media/i2c/ov7640.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2005-2006 Micronas USA Inc. + * + * 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> +#include <linux/slab.h> + +MODULE_DESCRIPTION("OmniVision ov7640 sensor driver"); +MODULE_LICENSE("GPL v2"); + +static const u8 initial_registers[] = { +	0x12, 0x80, +	0x12, 0x54, +	0x14, 0x24, +	0x15, 0x01, +	0x28, 0x20, +	0x75, 0x82, +	0xFF, 0xFF, /* Terminator (reg 0xFF is unused) */ +}; + +static int write_regs(struct i2c_client *client, const u8 *regs) +{ +	int i; + +	for (i = 0; regs[i] != 0xFF; i += 2) +		if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0) +			return -1; +	return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_ops ov7640_ops; + +static int ov7640_probe(struct i2c_client *client, +			const struct i2c_device_id *id) +{ +	struct i2c_adapter *adapter = client->adapter; +	struct v4l2_subdev *sd; + +	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) +		return -ENODEV; + +	sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); +	if (sd == NULL) +		return -ENOMEM; +	v4l2_i2c_subdev_init(sd, client, &ov7640_ops); + +	client->flags = I2C_CLIENT_SCCB; + +	v4l_info(client, "chip found @ 0x%02x (%s)\n", +			client->addr << 1, client->adapter->name); + +	if (write_regs(client, initial_registers) < 0) { +		v4l_err(client, "error initializing OV7640\n"); +		kfree(sd); +		return -ENODEV; +	} + +	return 0; +} + + +static int ov7640_remove(struct i2c_client *client) +{ +	struct v4l2_subdev *sd = i2c_get_clientdata(client); + +	v4l2_device_unregister_subdev(sd); +	kfree(sd); +	return 0; +} + +static const struct i2c_device_id ov7640_id[] = { +	{ "ov7640", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, ov7640_id); + +static struct i2c_driver ov7640_driver = { +	.driver = { +		.owner	= THIS_MODULE, +		.name	= "ov7640", +	}, +	.probe = ov7640_probe, +	.remove = ov7640_remove, +	.id_table = ov7640_id, +}; +module_i2c_driver(ov7640_driver); diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 05ed5b8e7f8..617ad3fff4a 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -1487,7 +1487,7 @@ static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *r  	return ret;  } -static int ov7670_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int ov7670_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index 5dbb65e1f6b..cb52438e53a 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -357,7 +357,7 @@ static int s5c73m3_load_fw(struct v4l2_subdev *sd)  		return -EINVAL;  	} -	v4l2_info(sd, "Loading firmware (%s, %d B)\n", fw_name, fw->size); +	v4l2_info(sd, "Loading firmware (%s, %zu B)\n", fw_name, fw->size);  	ret = s5c73m3_spi_write(state, fw->data, fw->size, 64); @@ -1457,6 +1457,12 @@ static int s5c73m3_oif_registered(struct v4l2_subdev *sd)  	return ret;  } +static void s5c73m3_oif_unregistered(struct v4l2_subdev *sd) +{ +	struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); +	v4l2_device_unregister_subdev(&state->sensor_sd); +} +  static const struct v4l2_subdev_internal_ops s5c73m3_internal_ops = {  	.open		= s5c73m3_open,  }; @@ -1474,6 +1480,7 @@ static const struct v4l2_subdev_ops s5c73m3_subdev_ops = {  static const struct v4l2_subdev_internal_ops oif_internal_ops = {  	.registered	= s5c73m3_oif_registered, +	.unregistered	= s5c73m3_oif_unregistered,  	.open		= s5c73m3_oif_open,  }; @@ -1668,13 +1675,17 @@ out_err1:  static int s5c73m3_remove(struct i2c_client *client)  { -	struct v4l2_subdev *sd = i2c_get_clientdata(client); -	struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); +	struct v4l2_subdev *oif_sd = i2c_get_clientdata(client); +	struct s5c73m3 *state = oif_sd_to_s5c73m3(oif_sd); +	struct v4l2_subdev *sensor_sd = &state->sensor_sd; -	v4l2_device_unregister_subdev(sd); +	v4l2_device_unregister_subdev(oif_sd); -	v4l2_ctrl_handler_free(sd->ctrl_handler); -	media_entity_cleanup(&sd->entity); +	v4l2_ctrl_handler_free(oif_sd->ctrl_handler); +	media_entity_cleanup(&oif_sd->entity); + +	v4l2_device_unregister_subdev(sensor_sd); +	media_entity_cleanup(&sensor_sd->entity);  	s5c73m3_unregister_spi_driver(state);  	s5c73m3_free_gpios(state); diff --git a/drivers/media/i2c/saa6588.c b/drivers/media/i2c/saa6588.c index 0caac50d7cf..b4e1ccbd87e 100644 --- a/drivers/media/i2c/saa6588.c +++ b/drivers/media/i2c/saa6588.c @@ -435,7 +435,7 @@ static int saa6588_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)  	return 0;  } -static int saa6588_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +static int saa6588_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt)  {  	struct saa6588 *s = to_saa6588(sd); diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c index 6b6788cd08f..52c717d977c 100644 --- a/drivers/media/i2c/saa7115.c +++ b/drivers/media/i2c/saa7115.c @@ -83,9 +83,10 @@ struct saa711x_state {  	u32 ident;  	u32 audclk_freq;  	u32 crystal_freq; -	u8 ucgc; +	bool ucgc;  	u8 cgcdiv; -	u8 apll; +	bool apll; +	bool double_asclk;  };  static inline struct saa711x_state *to_state(struct v4l2_subdev *sd) @@ -732,8 +733,12 @@ static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq)  	if (state->apll)  		acc |= 0x08; +	if (state->double_asclk) { +		acpf <<= 1; +		acni <<= 1; +	}  	saa711x_write(sd, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03); -	saa711x_write(sd, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10); +	saa711x_write(sd, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10 << state->double_asclk);  	saa711x_write(sd, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc);  	saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff); @@ -1259,6 +1264,12 @@ static int saa711x_s_routing(struct v4l2_subdev *sd,  				(saa711x_read(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) |  				(state->output & 0x01));  	} +	if (state->ident > V4L2_IDENT_SAA7111A) { +		if (config & SAA7115_IDQ_IS_DEFAULT) +			saa711x_write(sd, R_85_I_PORT_SIGNAL_POLAR, 0x20); +		else +			saa711x_write(sd, R_85_I_PORT_SIGNAL_POLAR, 0x21); +	}  	return 0;  } @@ -1296,9 +1307,10 @@ static int saa711x_s_crystal_freq(struct v4l2_subdev *sd, u32 freq, u32 flags)  	if (freq != SAA7115_FREQ_32_11_MHZ && freq != SAA7115_FREQ_24_576_MHZ)  		return -EINVAL;  	state->crystal_freq = freq; +	state->double_asclk = flags & SAA7115_FREQ_FL_DOUBLE_ASCLK;  	state->cgcdiv = (flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4; -	state->ucgc = (flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0; -	state->apll = (flags & SAA7115_FREQ_FL_APLL) ? 1 : 0; +	state->ucgc = flags & SAA7115_FREQ_FL_UCGC; +	state->apll = flags & SAA7115_FREQ_FL_APLL;  	saa711x_s_clock_freq(sd, state->audclk_freq);  	return 0;  } @@ -1354,6 +1366,34 @@ static int saa711x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)  	 */  	reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); + +	if (state->ident == V4L2_IDENT_SAA7115) { +		reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC); + +		v4l2_dbg(1, debug, sd, "Status byte 1 (0x1e)=0x%02x\n", reg1e); + +		switch (reg1e & 0x03) { +		case 1: +			*std &= V4L2_STD_NTSC; +			break; +		case 2: +			/* +			 * V4L2_STD_PAL just cover the european PAL standards. +			 * This is wrong, as the device could also be using an +			 * other PAL standard. +			 */ +			*std &= V4L2_STD_PAL   | V4L2_STD_PAL_N  | V4L2_STD_PAL_Nc | +				V4L2_STD_PAL_M | V4L2_STD_PAL_60; +			break; +		case 3: +			*std &= V4L2_STD_SECAM; +			break; +		default: +			/* Can't detect anything */ +			break; +		} +	} +  	v4l2_dbg(1, debug, sd, "Status byte 2 (0x1f)=0x%02x\n", reg1f);  	/* horizontal/vertical not locked */ @@ -1365,34 +1405,6 @@ static int saa711x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)  	else  		*std &= V4L2_STD_625_50; -	if (state->ident != V4L2_IDENT_SAA7115) -		goto ret; - -	reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC); - -	switch (reg1e & 0x03) { -	case 1: -		*std &= V4L2_STD_NTSC; -		break; -	case 2: -		/* -		 * V4L2_STD_PAL just cover the european PAL standards. -		 * This is wrong, as the device could also be using an -		 * other PAL standard. -		 */ -		*std &= V4L2_STD_PAL   | V4L2_STD_PAL_N  | V4L2_STD_PAL_Nc | -			V4L2_STD_PAL_M | V4L2_STD_PAL_60; -		break; -	case 3: -		*std &= V4L2_STD_SECAM; -		break; -	default: -		/* Can't detect anything */ -		break; -	} - -	v4l2_dbg(1, debug, sd, "Status byte 1 (0x1e)=0x%02x\n", reg1e); -  ret:  	v4l2_dbg(1, debug, sd, "detected std mask = %08Lx\n", *std); @@ -1428,7 +1440,7 @@ static int saa711x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *  	return 0;  } -static int saa711x_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int saa711x_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/saa7127.c b/drivers/media/i2c/saa7127.c index b745f68fbc9..8a47ac10927 100644 --- a/drivers/media/i2c/saa7127.c +++ b/drivers/media/i2c/saa7127.c @@ -672,7 +672,7 @@ static int saa7127_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *  	return 0;  } -static int saa7127_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int saa7127_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/saa717x.c b/drivers/media/i2c/saa717x.c index 1e84466515a..cf3a0aa7e45 100644 --- a/drivers/media/i2c/saa717x.c +++ b/drivers/media/i2c/saa717x.c @@ -988,7 +988,7 @@ static int saa717x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *  	return 0;  } -static int saa717x_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int saa717x_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd);  	u16 addr = reg->reg & 0xffff; @@ -1113,7 +1113,7 @@ static int saa717x_s_stream(struct v4l2_subdev *sd, int enable)  }  /* change audio mode */ -static int saa717x_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +static int saa717x_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt)  {  	struct saa717x_state *decoder = to_state(sd);  	int audio_mode; diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 83c7ed7ffcc..cae4f468385 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -2833,7 +2833,7 @@ static int smiapp_probe(struct i2c_client *client,  				 sensor->src->pads, 0);  } -static int __exit smiapp_remove(struct i2c_client *client) +static int smiapp_remove(struct i2c_client *client)  {  	struct v4l2_subdev *subdev = i2c_get_clientdata(client);  	struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); @@ -2881,7 +2881,7 @@ static struct i2c_driver smiapp_i2c_driver = {  		.pm = &smiapp_pm_ops,  	},  	.probe	= smiapp_probe, -	.remove	= __exit_p(smiapp_remove), +	.remove	= smiapp_remove,  	.id_table = smiapp_id_table,  }; diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig index 6dff2b7ad52..23d352f0adf 100644 --- a/drivers/media/i2c/soc_camera/Kconfig +++ b/drivers/media/i2c/soc_camera/Kconfig @@ -9,7 +9,6 @@ config SOC_CAMERA_IMX074  config SOC_CAMERA_MT9M001  	tristate "mt9m001 support"  	depends on SOC_CAMERA && I2C -	select GPIO_PCA953X if MT9M001_PCA9536_SWITCH  	help  	  This driver supports MT9M001 cameras from Micron, monochrome  	  and colour models. @@ -36,7 +35,6 @@ config SOC_CAMERA_MT9T112  config SOC_CAMERA_MT9V022  	tristate "mt9v022 and mt9v024 support"  	depends on SOC_CAMERA && I2C -	select GPIO_PCA953X if MT9V022_PCA9536_SWITCH  	help  	  This driver supports MT9V022 cameras from Micron diff --git a/drivers/media/i2c/soc_camera/mt9m001.c b/drivers/media/i2c/soc_camera/mt9m001.c index bcdc8617554..dd908980575 100644 --- a/drivers/media/i2c/soc_camera/mt9m001.c +++ b/drivers/media/i2c/soc_camera/mt9m001.c @@ -360,7 +360,7 @@ static int mt9m001_g_register(struct v4l2_subdev *sd,  }  static int mt9m001_s_register(struct v4l2_subdev *sd, -			      struct v4l2_dbg_register *reg) +			      const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/soc_camera/mt9m111.c b/drivers/media/i2c/soc_camera/mt9m111.c index bbc4ff99603..8bd4e0d2ea0 100644 --- a/drivers/media/i2c/soc_camera/mt9m111.c +++ b/drivers/media/i2c/soc_camera/mt9m111.c @@ -641,7 +641,7 @@ static int mt9m111_g_register(struct v4l2_subdev *sd,  }  static int mt9m111_s_register(struct v4l2_subdev *sd, -			      struct v4l2_dbg_register *reg) +			      const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -701,11 +701,11 @@ static int mt9m111_set_global_gain(struct mt9m111 *mt9m111, int gain)  	return reg_write(GLOBAL_GAIN, val);  } -static int mt9m111_set_autoexposure(struct mt9m111 *mt9m111, int on) +static int mt9m111_set_autoexposure(struct mt9m111 *mt9m111, int val)  {  	struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); -	if (on) +	if (val == V4L2_EXPOSURE_AUTO)  		return reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);  	return reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);  } @@ -785,8 +785,6 @@ static int mt9m111_init(struct mt9m111 *mt9m111)  	struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);  	int ret; -	/* Default HIGHPOWER context */ -	mt9m111->ctx = &context_b;  	ret = mt9m111_enable(mt9m111);  	if (!ret)  		ret = mt9m111_reset(mt9m111); @@ -975,6 +973,9 @@ static int mt9m111_probe(struct i2c_client *client,  	if (!mt9m111)  		return -ENOMEM; +	/* Default HIGHPOWER context */ +	mt9m111->ctx = &context_b; +  	v4l2_i2c_subdev_init(&mt9m111->subdev, client, &mt9m111_subdev_ops);  	v4l2_ctrl_handler_init(&mt9m111->hdl, 5);  	v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops, diff --git a/drivers/media/i2c/soc_camera/mt9t031.c b/drivers/media/i2c/soc_camera/mt9t031.c index d80d044ebf1..26a15b87a9a 100644 --- a/drivers/media/i2c/soc_camera/mt9t031.c +++ b/drivers/media/i2c/soc_camera/mt9t031.c @@ -430,7 +430,7 @@ static int mt9t031_g_register(struct v4l2_subdev *sd,  }  static int mt9t031_s_register(struct v4l2_subdev *sd, -			      struct v4l2_dbg_register *reg) +			      const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/soc_camera/mt9t112.c b/drivers/media/i2c/soc_camera/mt9t112.c index 188e29b0327..a7256b73280 100644 --- a/drivers/media/i2c/soc_camera/mt9t112.c +++ b/drivers/media/i2c/soc_camera/mt9t112.c @@ -766,7 +766,7 @@ static int mt9t112_g_register(struct v4l2_subdev *sd,  }  static int mt9t112_s_register(struct v4l2_subdev *sd, -			      struct v4l2_dbg_register *reg) +			      const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd);  	int ret; diff --git a/drivers/media/i2c/soc_camera/mt9v022.c b/drivers/media/i2c/soc_camera/mt9v022.c index a5e65d6a078..a295e598486 100644 --- a/drivers/media/i2c/soc_camera/mt9v022.c +++ b/drivers/media/i2c/soc_camera/mt9v022.c @@ -275,6 +275,7 @@ static int mt9v022_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)  	struct i2c_client *client = v4l2_get_subdevdata(sd);  	struct mt9v022 *mt9v022 = to_mt9v022(client);  	struct v4l2_rect rect = a->c; +	int min_row, min_blank;  	int ret;  	/* Bayer format - even size lengths */ @@ -310,13 +311,21 @@ static int mt9v022_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)  		ret = reg_write(client, MT9V022_COLUMN_START, rect.left);  	if (!ret)  		ret = reg_write(client, MT9V022_ROW_START, rect.top); +	/* +	 * mt9v022: min total row time is 660 columns, min blanking is 43 +	 * mt9v024: min total row time is 690 columns, min blanking is 61 +	 */ +	if (is_mt9v024(mt9v022->chip_version)) { +		min_row = 690; +		min_blank = 61; +	} else { +		min_row = 660; +		min_blank = 43; +	}  	if (!ret) -		/* -		 * Default 94, Phytec driver says: -		 * "width + horizontal blank >= 660" -		 */  		ret = v4l2_ctrl_s_ctrl(mt9v022->hblank, -				rect.width > 660 - 43 ? 43 : 660 - rect.width); +				rect.width > min_row - min_blank ? +				min_blank : min_row - rect.width);  	if (!ret)  		ret = v4l2_ctrl_s_ctrl(mt9v022->vblank, 45);  	if (!ret) @@ -488,7 +497,7 @@ static int mt9v022_g_register(struct v4l2_subdev *sd,  }  static int mt9v022_s_register(struct v4l2_subdev *sd, -			      struct v4l2_dbg_register *reg) +			      const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/soc_camera/ov2640.c b/drivers/media/i2c/soc_camera/ov2640.c index 0f520f693b6..e3168424f9b 100644 --- a/drivers/media/i2c/soc_camera/ov2640.c +++ b/drivers/media/i2c/soc_camera/ov2640.c @@ -756,7 +756,7 @@ static int ov2640_g_register(struct v4l2_subdev *sd,  }  static int ov2640_s_register(struct v4l2_subdev *sd, -			     struct v4l2_dbg_register *reg) +			     const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/soc_camera/ov5642.c b/drivers/media/i2c/soc_camera/ov5642.c index 9d53309619d..9aa56de69ee 100644 --- a/drivers/media/i2c/soc_camera/ov5642.c +++ b/drivers/media/i2c/soc_camera/ov5642.c @@ -708,7 +708,7 @@ static int ov5642_get_register(struct v4l2_subdev *sd, struct v4l2_dbg_register  	return ret;  } -static int ov5642_set_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int ov5642_set_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/soc_camera/ov6650.c b/drivers/media/i2c/soc_camera/ov6650.c index dbe4f564e6b..991202d4bba 100644 --- a/drivers/media/i2c/soc_camera/ov6650.c +++ b/drivers/media/i2c/soc_camera/ov6650.c @@ -421,7 +421,7 @@ static int ov6650_get_register(struct v4l2_subdev *sd,  }  static int ov6650_set_register(struct v4l2_subdev *sd, -				struct v4l2_dbg_register *reg) +				const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/soc_camera/ov772x.c b/drivers/media/i2c/soc_camera/ov772x.c index fbeb5b2f3ae..713d62e349f 100644 --- a/drivers/media/i2c/soc_camera/ov772x.c +++ b/drivers/media/i2c/soc_camera/ov772x.c @@ -652,7 +652,7 @@ static int ov772x_g_register(struct v4l2_subdev *sd,  }  static int ov772x_s_register(struct v4l2_subdev *sd, -			     struct v4l2_dbg_register *reg) +			     const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/soc_camera/ov9640.c b/drivers/media/i2c/soc_camera/ov9640.c index 05993041be3..20ca62d371c 100644 --- a/drivers/media/i2c/soc_camera/ov9640.c +++ b/drivers/media/i2c/soc_camera/ov9640.c @@ -322,7 +322,7 @@ static int ov9640_get_register(struct v4l2_subdev *sd,  }  static int ov9640_set_register(struct v4l2_subdev *sd, -				struct v4l2_dbg_register *reg) +				const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/soc_camera/ov9740.c b/drivers/media/i2c/soc_camera/ov9740.c index 2f236da8016..012bd627112 100644 --- a/drivers/media/i2c/soc_camera/ov9740.c +++ b/drivers/media/i2c/soc_camera/ov9740.c @@ -835,7 +835,7 @@ static int ov9740_get_register(struct v4l2_subdev *sd,  }  static int ov9740_set_register(struct v4l2_subdev *sd, -			       struct v4l2_dbg_register *reg) +			       const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/soc_camera/rj54n1cb0c.c b/drivers/media/i2c/soc_camera/rj54n1cb0c.c index 5c92679bfef..1f9ec3b06b4 100644 --- a/drivers/media/i2c/soc_camera/rj54n1cb0c.c +++ b/drivers/media/i2c/soc_camera/rj54n1cb0c.c @@ -1161,7 +1161,7 @@ static int rj54n1_g_register(struct v4l2_subdev *sd,  }  static int rj54n1_s_register(struct v4l2_subdev *sd, -			     struct v4l2_dbg_register *reg) +			     const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/soc_camera/tw9910.c b/drivers/media/i2c/soc_camera/tw9910.c index 7d207460188..bad90b16a6d 100644 --- a/drivers/media/i2c/soc_camera/tw9910.c +++ b/drivers/media/i2c/soc_camera/tw9910.c @@ -554,7 +554,7 @@ static int tw9910_g_register(struct v4l2_subdev *sd,  }  static int tw9910_s_register(struct v4l2_subdev *sd, -			     struct v4l2_dbg_register *reg) +			     const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/sony-btf-mpx.c b/drivers/media/i2c/sony-btf-mpx.c new file mode 100644 index 00000000000..38cbea98764 --- /dev/null +++ b/drivers/media/i2c/sony-btf-mpx.c @@ -0,0 +1,399 @@ +/* + * Copyright (C) 2005-2006 Micronas USA Inc. + * + * 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <media/tuner.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-device.h> +#include <linux/slab.h> + +MODULE_DESCRIPTION("sony-btf-mpx driver"); +MODULE_LICENSE("GPL v2"); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "debug level 0=off(default) 1=on\n"); + +/* #define MPX_DEBUG */ + +/* + * Note: + * + * AS(IF/MPX) pin:      LOW      HIGH/OPEN + * IF/MPX address:   0x42/0x40   0x43/0x44 + */ + + +static int force_mpx_mode = -1; +module_param(force_mpx_mode, int, 0644); + +struct sony_btf_mpx { +	struct v4l2_subdev sd; +	int mpxmode; +	u32 audmode; +}; + +static inline struct sony_btf_mpx *to_state(struct v4l2_subdev *sd) +{ +	return container_of(sd, struct sony_btf_mpx, sd); +} + +static int mpx_write(struct i2c_client *client, int dev, int addr, int val) +{ +	u8 buffer[5]; +	struct i2c_msg msg; + +	buffer[0] = dev; +	buffer[1] = addr >> 8; +	buffer[2] = addr & 0xff; +	buffer[3] = val >> 8; +	buffer[4] = val & 0xff; +	msg.addr = client->addr; +	msg.flags = 0; +	msg.len = 5; +	msg.buf = buffer; +	i2c_transfer(client->adapter, &msg, 1); +	return 0; +} + +/* + * MPX register values for the BTF-PG472Z: + * + *                                 FM_     NICAM_  SCART_ + *          MODUS  SOURCE    ACB   PRESCAL PRESCAL PRESCAL SYSTEM  VOLUME + *         10/0030 12/0008 12/0013 12/000E 12/0010 12/0000 10/0020 12/0000 + *         --------------------------------------------------------------- + * Auto     1003    0020    0100    2603    5000    XXXX    0001    7500 + * + * B/G + *  Mono    1003    0020    0100    2603    5000    XXXX    0003    7500 + *  A2      1003    0020    0100    2601    5000    XXXX    0003    7500 + *  NICAM   1003    0120    0100    2603    5000    XXXX    0008    7500 + * + * I + *  Mono    1003    0020    0100    2603    7900    XXXX    000A    7500 + *  NICAM   1003    0120    0100    2603    7900    XXXX    000A    7500 + * + * D/K + *  Mono    1003    0020    0100    2603    5000    XXXX    0004    7500 + *  A2-1    1003    0020    0100    2601    5000    XXXX    0004    7500 + *  A2-2    1003    0020    0100    2601    5000    XXXX    0005    7500 + *  A2-3    1003    0020    0100    2601    5000    XXXX    0007    7500 + *  NICAM   1003    0120    0100    2603    5000    XXXX    000B    7500 + * + * L/L' + *  Mono    0003    0200    0100    7C03    5000    2200    0009    7500 + *  NICAM   0003    0120    0100    7C03    5000    XXXX    0009    7500 + * + * M + *  Mono    1003    0200    0100    2B03    5000    2B00    0002    7500 + * + * For Asia, replace the 0x26XX in FM_PRESCALE with 0x14XX. + * + * Bilingual selection in A2/NICAM: + * + *         High byte of SOURCE     Left chan   Right chan + *                 0x01              MAIN         SUB + *                 0x03              MAIN         MAIN + *                 0x04              SUB          SUB + * + * Force mono in NICAM by setting the high byte of SOURCE to 0x02 (L/L') or + * 0x00 (all other bands).  Force mono in A2 with FMONO_A2: + * + *                      FMONO_A2 + *                      10/0022 + *                      -------- + *     Forced mono ON     07F0 + *     Forced mono OFF    0190 + */ + +static const struct { +	enum { AUD_MONO, AUD_A2, AUD_NICAM, AUD_NICAM_L } audio_mode; +	u16 modus; +	u16 source; +	u16 acb; +	u16 fm_prescale; +	u16 nicam_prescale; +	u16 scart_prescale; +	u16 system; +	u16 volume; +} mpx_audio_modes[] = { +	/* Auto */	{ AUD_MONO,	0x1003, 0x0020, 0x0100, 0x2603, +					0x5000, 0x0000, 0x0001, 0x7500 }, +	/* B/G Mono */	{ AUD_MONO,	0x1003, 0x0020, 0x0100, 0x2603, +					0x5000, 0x0000, 0x0003, 0x7500 }, +	/* B/G A2 */	{ AUD_A2,	0x1003, 0x0020, 0x0100, 0x2601, +					0x5000, 0x0000, 0x0003, 0x7500 }, +	/* B/G NICAM */ { AUD_NICAM,	0x1003, 0x0120, 0x0100, 0x2603, +					0x5000, 0x0000, 0x0008, 0x7500 }, +	/* I Mono */	{ AUD_MONO,	0x1003, 0x0020, 0x0100, 0x2603, +					0x7900, 0x0000, 0x000A, 0x7500 }, +	/* I NICAM */	{ AUD_NICAM,	0x1003, 0x0120, 0x0100, 0x2603, +					0x7900, 0x0000, 0x000A, 0x7500 }, +	/* D/K Mono */	{ AUD_MONO,	0x1003, 0x0020, 0x0100, 0x2603, +					0x5000, 0x0000, 0x0004, 0x7500 }, +	/* D/K A2-1 */	{ AUD_A2,	0x1003, 0x0020, 0x0100, 0x2601, +					0x5000, 0x0000, 0x0004, 0x7500 }, +	/* D/K A2-2 */	{ AUD_A2,	0x1003, 0x0020, 0x0100, 0x2601, +					0x5000, 0x0000, 0x0005, 0x7500 }, +	/* D/K A2-3 */	{ AUD_A2,	0x1003, 0x0020, 0x0100, 0x2601, +					0x5000, 0x0000, 0x0007, 0x7500 }, +	/* D/K NICAM */	{ AUD_NICAM,	0x1003, 0x0120, 0x0100, 0x2603, +					0x5000, 0x0000, 0x000B, 0x7500 }, +	/* L/L' Mono */	{ AUD_MONO,	0x0003, 0x0200, 0x0100, 0x7C03, +					0x5000, 0x2200, 0x0009, 0x7500 }, +	/* L/L' NICAM */{ AUD_NICAM_L,	0x0003, 0x0120, 0x0100, 0x7C03, +					0x5000, 0x0000, 0x0009, 0x7500 }, +}; + +#define MPX_NUM_MODES	ARRAY_SIZE(mpx_audio_modes) + +static int mpx_setup(struct sony_btf_mpx *t) +{ +	struct i2c_client *client = v4l2_get_subdevdata(&t->sd); +	u16 source = 0; +	u8 buffer[3]; +	struct i2c_msg msg; +	int mode = t->mpxmode; + +	/* reset MPX */ +	buffer[0] = 0x00; +	buffer[1] = 0x80; +	buffer[2] = 0x00; +	msg.addr = client->addr; +	msg.flags = 0; +	msg.len = 3; +	msg.buf = buffer; +	i2c_transfer(client->adapter, &msg, 1); +	buffer[1] = 0x00; +	i2c_transfer(client->adapter, &msg, 1); + +	if (t->audmode != V4L2_TUNER_MODE_MONO) +		mode++; + +	if (mpx_audio_modes[mode].audio_mode != AUD_MONO) { +		switch (t->audmode) { +		case V4L2_TUNER_MODE_MONO: +			switch (mpx_audio_modes[mode].audio_mode) { +			case AUD_A2: +				source = mpx_audio_modes[mode].source; +				break; +			case AUD_NICAM: +				source = 0x0000; +				break; +			case AUD_NICAM_L: +				source = 0x0200; +				break; +			default: +				break; +			} +			break; +		case V4L2_TUNER_MODE_STEREO: +			source = mpx_audio_modes[mode].source; +			break; +		case V4L2_TUNER_MODE_LANG1: +			source = 0x0300; +			break; +		case V4L2_TUNER_MODE_LANG2: +			source = 0x0400; +			break; +		} +		source |= mpx_audio_modes[mode].source & 0x00ff; +	} else +		source = mpx_audio_modes[mode].source; + +	mpx_write(client, 0x10, 0x0030, mpx_audio_modes[mode].modus); +	mpx_write(client, 0x12, 0x0008, source); +	mpx_write(client, 0x12, 0x0013, mpx_audio_modes[mode].acb); +	mpx_write(client, 0x12, 0x000e, +			mpx_audio_modes[mode].fm_prescale); +	mpx_write(client, 0x12, 0x0010, +			mpx_audio_modes[mode].nicam_prescale); +	mpx_write(client, 0x12, 0x000d, +			mpx_audio_modes[mode].scart_prescale); +	mpx_write(client, 0x10, 0x0020, mpx_audio_modes[mode].system); +	mpx_write(client, 0x12, 0x0000, mpx_audio_modes[mode].volume); +	if (mpx_audio_modes[mode].audio_mode == AUD_A2) +		mpx_write(client, 0x10, 0x0022, +			t->audmode == V4L2_TUNER_MODE_MONO ? 0x07f0 : 0x0190); + +#ifdef MPX_DEBUG +	{ +		u8 buf1[3], buf2[2]; +		struct i2c_msg msgs[2]; + +		v4l2_info(client, +			"MPX registers: %04x %04x %04x %04x %04x %04x %04x %04x\n", +			mpx_audio_modes[mode].modus, +			source, +			mpx_audio_modes[mode].acb, +			mpx_audio_modes[mode].fm_prescale, +			mpx_audio_modes[mode].nicam_prescale, +			mpx_audio_modes[mode].scart_prescale, +			mpx_audio_modes[mode].system, +			mpx_audio_modes[mode].volume); +		buf1[0] = 0x11; +		buf1[1] = 0x00; +		buf1[2] = 0x7e; +		msgs[0].addr = client->addr; +		msgs[0].flags = 0; +		msgs[0].len = 3; +		msgs[0].buf = buf1; +		msgs[1].addr = client->addr; +		msgs[1].flags = I2C_M_RD; +		msgs[1].len = 2; +		msgs[1].buf = buf2; +		i2c_transfer(client->adapter, msgs, 2); +		v4l2_info(client, "MPX system: %02x%02x\n", +				buf2[0], buf2[1]); +		buf1[0] = 0x11; +		buf1[1] = 0x02; +		buf1[2] = 0x00; +		i2c_transfer(client->adapter, msgs, 2); +		v4l2_info(client, "MPX status: %02x%02x\n", +				buf2[0], buf2[1]); +	} +#endif +	return 0; +} + + +static int sony_btf_mpx_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ +	struct sony_btf_mpx *t = to_state(sd); +	int default_mpx_mode = 0; + +	if (std & V4L2_STD_PAL_BG) +		default_mpx_mode = 1; +	else if (std & V4L2_STD_PAL_I) +		default_mpx_mode = 4; +	else if (std & V4L2_STD_PAL_DK) +		default_mpx_mode = 6; +	else if (std & V4L2_STD_SECAM_L) +		default_mpx_mode = 11; + +	if (default_mpx_mode != t->mpxmode) { +		t->mpxmode = default_mpx_mode; +		mpx_setup(t); +	} +	return 0; +} + +static int sony_btf_mpx_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ +	struct sony_btf_mpx *t = to_state(sd); + +	vt->capability = V4L2_TUNER_CAP_NORM | +		V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | +		V4L2_TUNER_CAP_LANG2; +	vt->rxsubchans = V4L2_TUNER_SUB_MONO | +		V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_LANG1 | +		V4L2_TUNER_SUB_LANG2; +	vt->audmode = t->audmode; +	return 0; +} + +static int sony_btf_mpx_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt) +{ +	struct sony_btf_mpx *t = to_state(sd); + +	if (vt->type != V4L2_TUNER_ANALOG_TV) +		return -EINVAL; + +	if (vt->audmode != t->audmode) { +		t->audmode = vt->audmode; +		mpx_setup(t); +	} +	return 0; +} + +/* --------------------------------------------------------------------------*/ + +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_ops sony_btf_mpx_ops = { +	.core = &sony_btf_mpx_core_ops, +	.tuner = &sony_btf_mpx_tuner_ops, +}; + +/* --------------------------------------------------------------------------*/ + +static int sony_btf_mpx_probe(struct i2c_client *client, +				const struct i2c_device_id *id) +{ +	struct sony_btf_mpx *t; +	struct v4l2_subdev *sd; + +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) +		return -ENODEV; + +	v4l_info(client, "chip found @ 0x%x (%s)\n", +			client->addr << 1, client->adapter->name); + +	t = kzalloc(sizeof(struct sony_btf_mpx), GFP_KERNEL); +	if (t == NULL) +		return -ENOMEM; + +	sd = &t->sd; +	v4l2_i2c_subdev_init(sd, client, &sony_btf_mpx_ops); + +	/* Initialize sony_btf_mpx */ +	t->mpxmode = 0; +	t->audmode = V4L2_TUNER_MODE_STEREO; + +	return 0; +} + +static int sony_btf_mpx_remove(struct i2c_client *client) +{ +	struct v4l2_subdev *sd = i2c_get_clientdata(client); + +	v4l2_device_unregister_subdev(sd); +	kfree(to_state(sd)); + +	return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id sony_btf_mpx_id[] = { +	{ "sony-btf-mpx", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, sony_btf_mpx_id); + +static struct i2c_driver sony_btf_mpx_driver = { +	.driver = { +		.owner	= THIS_MODULE, +		.name	= "sony-btf-mpx", +	}, +	.probe = sony_btf_mpx_probe, +	.remove = sony_btf_mpx_remove, +	.id_table = sony_btf_mpx_id, +}; +module_i2c_driver(sony_btf_mpx_driver); diff --git a/drivers/media/i2c/tda7432.c b/drivers/media/i2c/tda7432.c index f7707e65761..28b5121881f 100644 --- a/drivers/media/i2c/tda7432.c +++ b/drivers/media/i2c/tda7432.c @@ -35,6 +35,7 @@  #include <media/v4l2-device.h>  #include <media/v4l2-ioctl.h> +#include <media/v4l2-ctrls.h>  #include <media/i2c-addr.h>  #ifndef VIDEO_AUDIO_BALANCE @@ -60,13 +61,17 @@ MODULE_PARM_DESC(maxvol, "Set maximium volume to +20dB(0) else +0dB(1). Default  struct tda7432 {  	struct v4l2_subdev sd; -	int addr; -	int input; -	int volume; -	int muted; -	int bass, treble; -	int lf, lr, rf, rr; -	int loud; +	struct v4l2_ctrl_handler hdl; +	struct { +		/* bass/treble cluster */ +		struct v4l2_ctrl *bass; +		struct v4l2_ctrl *treble; +	}; +	struct { +		/* mute/balance cluster */ +		struct v4l2_ctrl *mute; +		struct v4l2_ctrl *balance; +	};  };  static inline struct tda7432 *to_state(struct v4l2_subdev *sd) @@ -74,6 +79,11 @@ static inline struct tda7432 *to_state(struct v4l2_subdev *sd)  	return container_of(sd, struct tda7432, sd);  } +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ +	return &container_of(ctrl->handler, struct tda7432, hdl)->sd; +} +  /* The TDA7432 is made by STS-Thompson   * http://www.st.com   * http://us.st.com/stonline/books/pdf/docs/4056.pdf @@ -227,24 +237,22 @@ static int tda7432_write(struct v4l2_subdev *sd, int subaddr, int val)  static int tda7432_set(struct v4l2_subdev *sd)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); -	struct tda7432 *t = to_state(sd);  	unsigned char buf[16]; -	v4l2_dbg(1, debug, sd, -		"tda7432: 7432_set(0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)\n", -		t->input, t->volume, t->bass, t->treble, t->lf, t->lr, -		t->rf, t->rr, t->loud);  	buf[0]  = TDA7432_IN; -	buf[1]  = t->input; -	buf[2]  = t->volume; -	buf[3]  = t->bass; -	buf[4]	= t->treble; -	buf[5]  = t->lf; -	buf[6]  = t->lr; -	buf[7]  = t->rf; -	buf[8]  = t->rr; -	buf[9]  = t->loud; -	if (10 != i2c_master_send(client, buf, 10)) { +	buf[1]  = TDA7432_STEREO_IN |  /* Main (stereo) input   */ +		  TDA7432_BASS_SYM  |  /* Symmetric bass cut    */ +		  TDA7432_BASS_NORM;   /* Normal bass range     */ +	buf[2]  = 0x3b; +	if (loudness)			 /* Turn loudness on?     */ +		buf[2] |= TDA7432_LD_ON; +	buf[3]  = TDA7432_TREBLE_0DB | (TDA7432_BASS_0DB << 4); +	buf[4]  = TDA7432_ATTEN_0DB; +	buf[5]  = TDA7432_ATTEN_0DB; +	buf[6]  = TDA7432_ATTEN_0DB; +	buf[7]  = TDA7432_ATTEN_0DB; +	buf[8]  = loudness; +	if (9 != i2c_master_send(client, buf, 9)) {  		v4l2_err(sd, "I/O error, trying tda7432_set\n");  		return -1;  	} @@ -252,174 +260,86 @@ static int tda7432_set(struct v4l2_subdev *sd)  	return 0;  } -static void do_tda7432_init(struct v4l2_subdev *sd) -{ -	struct tda7432 *t = to_state(sd); - -	v4l2_dbg(2, debug, sd, "In tda7432_init\n"); - -	t->input  = TDA7432_STEREO_IN |  /* Main (stereo) input   */ -		    TDA7432_BASS_SYM  |  /* Symmetric bass cut    */ -		    TDA7432_BASS_NORM;   /* Normal bass range     */ -	t->volume =  0x3b ;				 /* -27dB Volume            */ -	if (loudness)			 /* Turn loudness on?     */ -		t->volume |= TDA7432_LD_ON; -	t->muted    = 1; -	t->treble   = TDA7432_TREBLE_0DB; /* 0dB Treble            */ -	t->bass		= TDA7432_BASS_0DB; 	 /* 0dB Bass              */ -	t->lf     = TDA7432_ATTEN_0DB;	 /* 0dB attenuation       */ -	t->lr     = TDA7432_ATTEN_0DB;	 /* 0dB attenuation       */ -	t->rf     = TDA7432_ATTEN_0DB;	 /* 0dB attenuation       */ -	t->rr     = TDA7432_ATTEN_0DB;	 /* 0dB attenuation       */ -	t->loud   = loudness;		 /* insmod parameter      */ - -	tda7432_set(sd); -} - -static int tda7432_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int tda7432_log_status(struct v4l2_subdev *sd)  { -	struct tda7432 *t = to_state(sd); +	struct tda7432 *state = to_state(sd); -	switch (ctrl->id) { -	case V4L2_CID_AUDIO_MUTE: -		ctrl->value=t->muted; -		return 0; -	case V4L2_CID_AUDIO_VOLUME: -		if (!maxvol){  /* max +20db */ -			ctrl->value = ( 0x6f - (t->volume & 0x7F) ) * 630; -		} else {       /* max 0db   */ -			ctrl->value = ( 0x6f - (t->volume & 0x7F) ) * 829; -		} -		return 0; -	case V4L2_CID_AUDIO_BALANCE: -	{ -		if ( (t->lf) < (t->rf) ) -			/* right is attenuated, balance shifted left */ -			ctrl->value = (32768 - 1057*(t->rf)); -		else -			/* left is attenuated, balance shifted right */ -			ctrl->value = (32768 + 1057*(t->lf)); -		return 0; -	} -	case V4L2_CID_AUDIO_BASS: -	{ -		/* Bass/treble 4 bits each */ -		int bass=t->bass; -		if(bass >= 0x8) -			bass = ~(bass - 0x8) & 0xf; -		ctrl->value = (bass << 12)+(bass << 8)+(bass << 4)+(bass); -		return 0; -	} -	case V4L2_CID_AUDIO_TREBLE: -	{ -		int treble=t->treble; -		if(treble >= 0x8) -			treble = ~(treble - 0x8) & 0xf; -		ctrl->value = (treble << 12)+(treble << 8)+(treble << 4)+(treble); -		return 0; -	} -	} -	return -EINVAL; +	v4l2_ctrl_handler_log_status(&state->hdl, sd->name); +	return 0;  } -static int tda7432_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int tda7432_s_ctrl(struct v4l2_ctrl *ctrl)  { +	struct v4l2_subdev *sd = to_sd(ctrl);  	struct tda7432 *t = to_state(sd); +	u8 bass, treble, volume; +	u8 lf, lr, rf, rr;  	switch (ctrl->id) {  	case V4L2_CID_AUDIO_MUTE: -		t->muted=ctrl->value; -		break; -	case V4L2_CID_AUDIO_VOLUME: -		if(!maxvol){ /* max +20db */ -			t->volume = 0x6f - ((ctrl->value)/630); -		} else {    /* max 0db   */ -			t->volume = 0x6f - ((ctrl->value)/829); -		} -		if (loudness)		/* Turn on the loudness bit */ -			t->volume |= TDA7432_LD_ON; - -		tda7432_write(sd, TDA7432_VL, t->volume); -		return 0; -	case V4L2_CID_AUDIO_BALANCE: -		if (ctrl->value < 32768) { +		if (t->balance->val < 0) {  			/* shifted to left, attenuate right */ -			t->rr = (32768 - ctrl->value)/1057; -			t->rf = t->rr; -			t->lr = TDA7432_ATTEN_0DB; -			t->lf = TDA7432_ATTEN_0DB; -		} else if(ctrl->value > 32769) { +			rr = rf = -t->balance->val; +			lr = lf = TDA7432_ATTEN_0DB; +		} else if (t->balance->val > 0) {  			/* shifted to right, attenuate left */ -			t->lf = (ctrl->value - 32768)/1057; -			t->lr = t->lf; -			t->rr = TDA7432_ATTEN_0DB; -			t->rf = TDA7432_ATTEN_0DB; +			rr = rf = TDA7432_ATTEN_0DB; +			lr = lf = t->balance->val;  		} else {  			/* centered */ -			t->rr = TDA7432_ATTEN_0DB; -			t->rf = TDA7432_ATTEN_0DB; -			t->lf = TDA7432_ATTEN_0DB; -			t->lr = TDA7432_ATTEN_0DB; +			rr = rf = TDA7432_ATTEN_0DB; +			lr = lf = TDA7432_ATTEN_0DB;  		} -		break; -	case V4L2_CID_AUDIO_BASS: -		t->bass = ctrl->value >> 12; -		if(t->bass>= 0x8) -				t->bass = (~t->bass & 0xf) + 0x8 ; - -		tda7432_write(sd, TDA7432_TN, 0x10 | (t->bass << 4) | t->treble); +		if (t->mute->val) { +			lf |= TDA7432_MUTE; +			lr |= TDA7432_MUTE; +			lf |= TDA7432_MUTE; +			rr |= TDA7432_MUTE; +		} +		/* Mute & update balance*/ +		tda7432_write(sd, TDA7432_LF, lf); +		tda7432_write(sd, TDA7432_LR, lr); +		tda7432_write(sd, TDA7432_RF, rf); +		tda7432_write(sd, TDA7432_RR, rr);  		return 0; -	case V4L2_CID_AUDIO_TREBLE: -		t->treble= ctrl->value >> 12; -		if(t->treble>= 0x8) -				t->treble = (~t->treble & 0xf) + 0x8 ; +	case V4L2_CID_AUDIO_VOLUME: +		volume = 0x6f - ctrl->val; +		if (loudness)		/* Turn on the loudness bit */ +			volume |= TDA7432_LD_ON; -		tda7432_write(sd, TDA7432_TN, 0x10 | (t->bass << 4) | t->treble); +		tda7432_write(sd, TDA7432_VL, volume);  		return 0; -	default: -		return -EINVAL; -	} - -	/* Used for both mute and balance changes */ -	if (t->muted) -	{ -		/* Mute & update balance*/ -		tda7432_write(sd, TDA7432_LF, t->lf | TDA7432_MUTE); -		tda7432_write(sd, TDA7432_LR, t->lr | TDA7432_MUTE); -		tda7432_write(sd, TDA7432_RF, t->rf | TDA7432_MUTE); -		tda7432_write(sd, TDA7432_RR, t->rr | TDA7432_MUTE); -	} else { -		tda7432_write(sd, TDA7432_LF, t->lf); -		tda7432_write(sd, TDA7432_LR, t->lr); -		tda7432_write(sd, TDA7432_RF, t->rf); -		tda7432_write(sd, TDA7432_RR, t->rr); -	} -	return 0; -} - -static int tda7432_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ -	switch (qc->id) { -	case V4L2_CID_AUDIO_VOLUME: -		return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880); -	case V4L2_CID_AUDIO_MUTE: -		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); -	case V4L2_CID_AUDIO_BALANCE:  	case V4L2_CID_AUDIO_BASS: -	case V4L2_CID_AUDIO_TREBLE: -		return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); +		bass = t->bass->val; +		treble = t->treble->val; +		if (bass >= 0x8) +			bass = 14 - (bass - 8); +		if (treble >= 0x8) +			treble = 14 - (treble - 8); + +		tda7432_write(sd, TDA7432_TN, 0x10 | (bass << 4) | treble); +		return 0;  	}  	return -EINVAL;  }  /* ----------------------------------------------------------------------- */ -static const struct v4l2_subdev_core_ops tda7432_core_ops = { -	.queryctrl = tda7432_queryctrl, -	.g_ctrl = tda7432_g_ctrl, +static const struct v4l2_ctrl_ops tda7432_ctrl_ops = {  	.s_ctrl = tda7432_s_ctrl,  }; +static const struct v4l2_subdev_core_ops tda7432_core_ops = { +	.log_status = tda7432_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, +}; +  static const struct v4l2_subdev_ops tda7432_ops = {  	.core = &tda7432_core_ops,  }; @@ -444,6 +364,28 @@ static int tda7432_probe(struct i2c_client *client,  		return -ENOMEM;  	sd = &t->sd;  	v4l2_i2c_subdev_init(sd, client, &tda7432_ops); +	v4l2_ctrl_handler_init(&t->hdl, 5); +	v4l2_ctrl_new_std(&t->hdl, &tda7432_ctrl_ops, +		V4L2_CID_AUDIO_VOLUME, 0, maxvol ? 0x68 : 0x4f, 1, maxvol ? 0x5d : 0x47); +	t->mute = v4l2_ctrl_new_std(&t->hdl, &tda7432_ctrl_ops, +		V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); +	t->balance = v4l2_ctrl_new_std(&t->hdl, &tda7432_ctrl_ops, +		V4L2_CID_AUDIO_BALANCE, -31, 31, 1, 0); +	t->bass = v4l2_ctrl_new_std(&t->hdl, &tda7432_ctrl_ops, +		V4L2_CID_AUDIO_BASS, 0, 14, 1, 7); +	t->treble = v4l2_ctrl_new_std(&t->hdl, &tda7432_ctrl_ops, +		V4L2_CID_AUDIO_TREBLE, 0, 14, 1, 7); +	sd->ctrl_handler = &t->hdl; +	if (t->hdl.error) { +		int err = t->hdl.error; + +		v4l2_ctrl_handler_free(&t->hdl); +		kfree(t); +		return err; +	} +	v4l2_ctrl_cluster(2, &t->bass); +	v4l2_ctrl_cluster(2, &t->mute); +	v4l2_ctrl_handler_setup(&t->hdl);  	if (loudness < 0 || loudness > 15) {  		v4l2_warn(sd, "loudness parameter must be between 0 and 15\n");  		if (loudness < 0) @@ -452,17 +394,19 @@ static int tda7432_probe(struct i2c_client *client,  			loudness = 15;  	} -	do_tda7432_init(sd); +	tda7432_set(sd);  	return 0;  }  static int tda7432_remove(struct i2c_client *client)  {  	struct v4l2_subdev *sd = i2c_get_clientdata(client); +	struct tda7432 *t = to_state(sd); -	do_tda7432_init(sd); +	tda7432_set(sd);  	v4l2_device_unregister_subdev(sd); -	kfree(to_state(sd)); +	v4l2_ctrl_handler_free(&t->hdl); +	kfree(t);  	return 0;  } diff --git a/drivers/media/i2c/tda9840.c b/drivers/media/i2c/tda9840.c index 3d7ddd93282..01441e35d88 100644 --- a/drivers/media/i2c/tda9840.c +++ b/drivers/media/i2c/tda9840.c @@ -87,7 +87,7 @@ static int tda9840_status(struct v4l2_subdev *sd)  	return byte & 0x60;  } -static int tda9840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) +static int tda9840_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *t)  {  	int stat = tda9840_status(sd);  	int byte; diff --git a/drivers/media/i2c/ths7303.c b/drivers/media/i2c/ths7303.c index e747524ba6e..c4339556a2e 100644 --- a/drivers/media/i2c/ths7303.c +++ b/drivers/media/i2c/ths7303.c @@ -1,7 +1,15 @@  /* - * ths7303- THS7303 Video Amplifier driver + * ths7303/53- THS7303/53 Video Amplifier driver   *   * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. + * + * Author: Chaithrika U S <chaithrika@ti.com> + * + * Contributors: + *     Hans Verkuil <hans.verkuil@cisco.com> + *     Lad, Prabhakar <prabhakar.lad@ti.com> + *     Martin Bugge <marbugge@cisco.com>   *   * This program is free software; you can redistribute it and/or   * modify it under the terms of the GNU General Public License as @@ -13,25 +21,27 @@   * GNU General Public License for more details.   */ -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/ctype.h> -#include <linux/slab.h>  #include <linux/i2c.h> -#include <linux/device.h> -#include <linux/delay.h>  #include <linux/module.h> -#include <linux/uaccess.h> -#include <linux/videodev2.h> +#include <linux/slab.h> -#include <media/v4l2-device.h> -#include <media/v4l2-subdev.h> +#include <media/ths7303.h>  #include <media/v4l2-chip-ident.h> +#include <media/v4l2-device.h>  #define THS7303_CHANNEL_1	1  #define THS7303_CHANNEL_2	2  #define THS7303_CHANNEL_3	3 +struct ths7303_state { +	struct v4l2_subdev		sd; +	struct ths7303_platform_data	pdata; +	struct v4l2_bt_timings		bt; +	int std_id; +	int stream_on; +	int driver_data; +}; +  enum ths7303_filter_mode {  	THS7303_FILTER_MODE_480I_576I,  	THS7303_FILTER_MODE_480P_576P, @@ -48,64 +58,84 @@ static int debug;  module_param(debug, int, 0644);  MODULE_PARM_DESC(debug, "Debug level 0-1"); +static inline struct ths7303_state *to_state(struct v4l2_subdev *sd) +{ +	return container_of(sd, struct ths7303_state, sd); +} + +static int ths7303_read(struct v4l2_subdev *sd, u8 reg) +{ +	struct i2c_client *client = v4l2_get_subdevdata(sd); + +	return i2c_smbus_read_byte_data(client, reg); +} + +static int ths7303_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ +	struct i2c_client *client = v4l2_get_subdevdata(sd); +	int ret; +	int i; + +	for (i = 0; i < 3; i++) { +		ret = i2c_smbus_write_byte_data(client, reg, val); +		if (ret == 0) +			return 0; +	} +	return ret; +} +  /* following function is used to set ths7303 */  int ths7303_setval(struct v4l2_subdev *sd, enum ths7303_filter_mode mode)  { -	u8 input_bias_chroma = 3; -	u8 input_bias_luma = 3; -	int disable = 0; -	int err = 0; -	u8 val = 0; -	u8 temp; -  	struct i2c_client *client = v4l2_get_subdevdata(sd); +	struct ths7303_state *state = to_state(sd); +	struct ths7303_platform_data *pdata = &state->pdata; +	u8 val, sel = 0; +	int err, disable = 0;  	if (!client)  		return -EINVAL;  	switch (mode) {  	case THS7303_FILTER_MODE_1080P: -		val = (3 << 6); -		val |= (3 << 3); +		sel = 0x3;	/*1080p and SXGA/UXGA */  		break;  	case THS7303_FILTER_MODE_720P_1080I: -		val = (2 << 6); -		val |= (2 << 3); +		sel = 0x2;	/*720p, 1080i and SVGA/XGA */  		break;  	case THS7303_FILTER_MODE_480P_576P: -		val = (1 << 6); -		val |= (1 << 3); +		sel = 0x1;	/* EDTV 480p/576p and VGA */  		break;  	case THS7303_FILTER_MODE_480I_576I: +		sel = 0x0;	/* SDTV, S-Video, 480i/576i */  		break; -	case THS7303_FILTER_MODE_DISABLE: -		pr_info("mode disabled\n"); -		/* disable all channels */ -		disable = 1;  	default:  		/* disable all channels */  		disable = 1;  	} -	/* Setup channel 2 - Luma - Green */ -	temp = val; + +	val = (sel << 6) | (sel << 3);  	if (!disable) -		val |= input_bias_luma; -	err = i2c_smbus_write_byte_data(client, THS7303_CHANNEL_2, val); +		val |= (pdata->ch_1 & 0x27); +	err = ths7303_write(sd, THS7303_CHANNEL_1, val);  	if (err)  		goto out; -	/* setup two chroma channels */ +	val = (sel << 6) | (sel << 3);  	if (!disable) -		temp |= input_bias_chroma; - -	err = i2c_smbus_write_byte_data(client, THS7303_CHANNEL_1, temp); +		val |= (pdata->ch_2 & 0x27); +	err = ths7303_write(sd, THS7303_CHANNEL_2, val);  	if (err)  		goto out; -	err = i2c_smbus_write_byte_data(client, THS7303_CHANNEL_3, temp); +	val = (sel << 6) | (sel << 3); +	if (!disable) +		val |= (pdata->ch_3 & 0x27); +	err = ths7303_write(sd, THS7303_CHANNEL_3, val);  	if (err)  		goto out; -	return err; + +	return 0;  out:  	pr_info("write byte data failed\n");  	return err; @@ -113,49 +143,209 @@ out:  static int ths7303_s_std_output(struct v4l2_subdev *sd, v4l2_std_id norm)  { -	if (norm & (V4L2_STD_ALL & ~V4L2_STD_SECAM)) +	struct ths7303_state *state = to_state(sd); + +	if (norm & (V4L2_STD_ALL & ~V4L2_STD_SECAM)) { +		state->std_id = 1; +		state->bt.pixelclock = 0;  		return ths7303_setval(sd, THS7303_FILTER_MODE_480I_576I); -	else -		return ths7303_setval(sd, THS7303_FILTER_MODE_DISABLE); +	} + +	return ths7303_setval(sd, THS7303_FILTER_MODE_DISABLE);  } -/* for setting filter for HD output */ -static int ths7303_s_dv_timings(struct v4l2_subdev *sd, -			       struct v4l2_dv_timings *dv_timings) +static int ths7303_config(struct v4l2_subdev *sd)  { -	u32 height = dv_timings->bt.height; -	int interlaced = dv_timings->bt.interlaced; -	int res = 0; +	struct ths7303_state *state = to_state(sd); +	int res; + +	if (!state->stream_on) { +		ths7303_write(sd, THS7303_CHANNEL_1, +			      (ths7303_read(sd, THS7303_CHANNEL_1) & 0xf8) | +			      0x00); +		ths7303_write(sd, THS7303_CHANNEL_2, +			      (ths7303_read(sd, THS7303_CHANNEL_2) & 0xf8) | +			      0x00); +		ths7303_write(sd, THS7303_CHANNEL_3, +			      (ths7303_read(sd, THS7303_CHANNEL_3) & 0xf8) | +			      0x00); +		return 0; +	} -	if (height == 1080 && !interlaced) +	if (state->bt.pixelclock > 120000000)  		res = ths7303_setval(sd, THS7303_FILTER_MODE_1080P); -	else if ((height == 720 && !interlaced) || -			(height == 1080 && interlaced)) +	else if (state->bt.pixelclock > 70000000)  		res = ths7303_setval(sd, THS7303_FILTER_MODE_720P_1080I); -	else if ((height == 480 || height == 576) && !interlaced) +	else if (state->bt.pixelclock > 20000000)  		res = ths7303_setval(sd, THS7303_FILTER_MODE_480P_576P); +	else if (state->std_id) +		res = ths7303_setval(sd, THS7303_FILTER_MODE_480I_576I);  	else  		/* disable all channels */  		res = ths7303_setval(sd, THS7303_FILTER_MODE_DISABLE);  	return res; + +} + +static int ths7303_s_stream(struct v4l2_subdev *sd, int enable) +{ +	struct ths7303_state *state = to_state(sd); + +	state->stream_on = enable; + +	return ths7303_config(sd); +} + +/* for setting filter for HD output */ +static int ths7303_s_dv_timings(struct v4l2_subdev *sd, +			       struct v4l2_dv_timings *dv_timings) +{ +	struct ths7303_state *state = to_state(sd); + +	if (!dv_timings || dv_timings->type != V4L2_DV_BT_656_1120) +		return -EINVAL; + +	state->bt = dv_timings->bt; +	state->std_id = 0; + +	return ths7303_config(sd);  }  static int ths7303_g_chip_ident(struct v4l2_subdev *sd,  				struct v4l2_dbg_chip_ident *chip)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); +	struct ths7303_state *state = to_state(sd); -	return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_THS7303, 0); +	return v4l2_chip_ident_i2c_client(client, chip, state->driver_data, 0);  }  static const struct v4l2_subdev_video_ops ths7303_video_ops = { +	.s_stream	= ths7303_s_stream,  	.s_std_output	= ths7303_s_std_output, -	.s_dv_timings    = ths7303_s_dv_timings, +	.s_dv_timings   = ths7303_s_dv_timings,  }; +#ifdef CONFIG_VIDEO_ADV_DEBUG + +static int ths7303_g_register(struct v4l2_subdev *sd, +			      struct v4l2_dbg_register *reg) +{ +	struct i2c_client *client = v4l2_get_subdevdata(sd); + +	if (!v4l2_chip_match_i2c_client(client, ®->match)) +		return -EINVAL; +	if (!capable(CAP_SYS_ADMIN)) +		return -EPERM; + +	reg->size = 1; +	reg->val = ths7303_read(sd, reg->reg); +	return 0; +} + +static int ths7303_s_register(struct v4l2_subdev *sd, +			      const struct v4l2_dbg_register *reg) +{ +	struct i2c_client *client = v4l2_get_subdevdata(sd); + +	if (!v4l2_chip_match_i2c_client(client, ®->match)) +		return -EINVAL; +	if (!capable(CAP_SYS_ADMIN)) +		return -EPERM; + +	ths7303_write(sd, reg->reg, reg->val); +	return 0; +} +#endif + +static const char * const stc_lpf_sel_txt[4] = { +	"500-kHz Filter", +	"2.5-MHz Filter", +	"5-MHz Filter", +	"5-MHz Filter", +}; + +static const char * const in_mux_sel_txt[2] = { +	"Input A Select", +	"Input B Select", +}; + +static const char * const lpf_freq_sel_txt[4] = { +	"9-MHz LPF", +	"16-MHz LPF", +	"35-MHz LPF", +	"Bypass LPF", +}; + +static const char * const in_bias_sel_dis_cont_txt[8] = { +	"Disable Channel", +	"Mute Function - No Output", +	"DC Bias Select", +	"DC Bias + 250 mV Offset Select", +	"AC Bias Select", +	"Sync Tip Clamp with low bias", +	"Sync Tip Clamp with mid bias", +	"Sync Tip Clamp with high bias", +}; + +static void ths7303_log_channel_status(struct v4l2_subdev *sd, u8 reg) +{ +	u8 val = ths7303_read(sd, reg); + +	if ((val & 0x7) == 0) { +		v4l2_info(sd, "Channel %d Off\n", reg); +		return; +	} + +	v4l2_info(sd, "Channel %d On\n", reg); +	v4l2_info(sd, "  value 0x%x\n", val); +	v4l2_info(sd, "  %s\n", stc_lpf_sel_txt[(val >> 6) & 0x3]); +	v4l2_info(sd, "  %s\n", in_mux_sel_txt[(val >> 5) & 0x1]); +	v4l2_info(sd, "  %s\n", lpf_freq_sel_txt[(val >> 3) & 0x3]); +	v4l2_info(sd, "  %s\n", in_bias_sel_dis_cont_txt[(val >> 0) & 0x7]); +} + +static int ths7303_log_status(struct v4l2_subdev *sd) +{ +	struct ths7303_state *state = to_state(sd); + +	v4l2_info(sd, "stream %s\n", state->stream_on ? "On" : "Off"); + +	if (state->bt.pixelclock) { +		struct v4l2_bt_timings *bt = bt = &state->bt; +		u32 frame_width, frame_height; + +		frame_width = bt->width + bt->hfrontporch + +			      bt->hsync + bt->hbackporch; +		frame_height = bt->height + bt->vfrontporch + +			       bt->vsync + bt->vbackporch; +		v4l2_info(sd, +			  "timings: %dx%d%s%d (%dx%d). Pix freq. = %d Hz. Polarities = 0x%x\n", +			  bt->width, bt->height, bt->interlaced ? "i" : "p", +			  (frame_height * frame_width) > 0 ? +			  (int)bt->pixelclock / +			  (frame_height * frame_width) : 0, +			  frame_width, frame_height, +			  (int)bt->pixelclock, bt->polarities); +	} else { +		v4l2_info(sd, "no timings set\n"); +	} + +	ths7303_log_channel_status(sd, THS7303_CHANNEL_1); +	ths7303_log_channel_status(sd, THS7303_CHANNEL_2); +	ths7303_log_channel_status(sd, THS7303_CHANNEL_3); + +	return 0; +} +  static const struct v4l2_subdev_core_ops ths7303_core_ops = {  	.g_chip_ident = ths7303_g_chip_ident, +	.log_status = ths7303_log_status, +#ifdef CONFIG_VIDEO_ADV_DEBUG +	.g_register = ths7303_g_register, +	.s_register = ths7303_s_register, +#endif  };  static const struct v4l2_subdev_ops ths7303_ops = { @@ -163,11 +353,38 @@ static const struct v4l2_subdev_ops ths7303_ops = {  	.video 	= &ths7303_video_ops,  }; +static int ths7303_setup(struct v4l2_subdev *sd) +{ +	struct ths7303_state *state = to_state(sd); +	struct ths7303_platform_data *pdata = &state->pdata; +	int ret; +	u8 mask; + +	state->stream_on = pdata->init_enable; + +	mask = state->stream_on ? 0xff : 0xf8; + +	ret = ths7303_write(sd, THS7303_CHANNEL_1, pdata->ch_1 & mask); +	if (ret) +		return ret; + +	ret = ths7303_write(sd, THS7303_CHANNEL_2, pdata->ch_2 & mask); +	if (ret) +		return ret; + +	ret = ths7303_write(sd, THS7303_CHANNEL_3, pdata->ch_3 & mask); +	if (ret) +		return ret; + +	return 0; +} +  static int ths7303_probe(struct i2c_client *client,  			const struct i2c_device_id *id)  { +	struct ths7303_platform_data *pdata = client->dev.platform_data; +	struct ths7303_state *state;  	struct v4l2_subdev *sd; -	v4l2_std_id std_id = V4L2_STD_NTSC;  	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))  		return -ENODEV; @@ -175,13 +392,28 @@ static int ths7303_probe(struct i2c_client *client,  	v4l_info(client, "chip found @ 0x%x (%s)\n",  			client->addr << 1, client->adapter->name); -	sd = devm_kzalloc(&client->dev, sizeof(struct v4l2_subdev), GFP_KERNEL); -	if (sd == NULL) +	state = devm_kzalloc(&client->dev, sizeof(struct ths7303_state), +			     GFP_KERNEL); +	if (!state)  		return -ENOMEM; +	if (!pdata) +		v4l_warn(client, "No platform data, using default data!\n"); +	else +		state->pdata = *pdata; + +	sd = &state->sd;  	v4l2_i2c_subdev_init(sd, client, &ths7303_ops); -	return ths7303_s_std_output(sd, std_id); +	/* store the driver data to differntiate the chip */ +	state->driver_data = (int)id->driver_data; + +	if (ths7303_setup(sd) < 0) { +		v4l_err(client, "init failed\n"); +		return -EIO; +	} + +	return 0;  }  static int ths7303_remove(struct i2c_client *client) @@ -194,7 +426,8 @@ static int ths7303_remove(struct i2c_client *client)  }  static const struct i2c_device_id ths7303_id[] = { -	{"ths7303", 0}, +	{"ths7303", V4L2_IDENT_THS7303}, +	{"ths7353", V4L2_IDENT_THS7353},  	{},  }; @@ -203,7 +436,7 @@ MODULE_DEVICE_TABLE(i2c, ths7303_id);  static struct i2c_driver ths7303_driver = {  	.driver = {  		.owner	= THIS_MODULE, -		.name	= "ths7303", +		.name	= "ths73x3",  	},  	.probe		= ths7303_probe,  	.remove		= ths7303_remove, diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c index e3b33b78dd2..b72a59d3216 100644 --- a/drivers/media/i2c/tvaudio.c +++ b/drivers/media/i2c/tvaudio.c @@ -1761,7 +1761,7 @@ static int tvaudio_s_routing(struct v4l2_subdev *sd,  	return 0;  } -static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +static int tvaudio_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt)  {  	struct CHIPSTATE *chip = to_state(sd);  	struct CHIPDESC *desc = chip->desc; @@ -1803,7 +1803,7 @@ static int tvaudio_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)  	vt->audmode = chip->audmode;  	vt->rxsubchans = desc->getrxsubchans(chip); -	vt->capability = V4L2_TUNER_CAP_STEREO | +	vt->capability |= V4L2_TUNER_CAP_STEREO |  		V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;  	return 0; @@ -1817,7 +1817,7 @@ static int tvaudio_s_std(struct v4l2_subdev *sd, v4l2_std_id std)  	return 0;  } -static int tvaudio_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +static int tvaudio_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequency *freq)  {  	struct CHIPSTATE *chip = to_state(sd);  	struct CHIPDESC *desc = chip->desc; diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c index aa94ebc2d75..ab8f3fee7e9 100644 --- a/drivers/media/i2c/tvp514x.c +++ b/drivers/media/i2c/tvp514x.c @@ -12,6 +12,7 @@   *     Hardik Shah <hardik.shah@ti.com>   *     Manjunath Hadli <mrh@ti.com>   *     Karicheri Muralidharan <m-karicheri2@ti.com> + *     Prabhakar Lad <prabhakar.lad@ti.com>   *   * This package is free software; you can redistribute it and/or modify   * it under the terms of the GNU General Public License version 2 as @@ -33,6 +34,7 @@  #include <linux/delay.h>  #include <linux/videodev2.h>  #include <linux/module.h> +#include <linux/v4l2-mediabus.h>  #include <media/v4l2-device.h>  #include <media/v4l2-common.h> @@ -40,12 +42,10 @@  #include <media/v4l2-chip-ident.h>  #include <media/v4l2-ctrls.h>  #include <media/tvp514x.h> +#include <media/media-entity.h>  #include "tvp514x_regs.h" -/* Module Name */ -#define TVP514X_MODULE_NAME		"tvp514x" -  /* Private macros for TVP */  #define I2C_RETRY_COUNT                 (5)  #define LOCK_RETRY_COUNT                (5) @@ -91,6 +91,9 @@ static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable);   * @pdata: Board specific   * @ver: Chip version   * @streaming: TVP5146/47 decoder streaming - enabled or disabled. + * @pix: Current pixel format + * @num_fmts: Number of formats + * @fmt_list: Format list   * @current_std: Current standard   * @num_stds: Number of standards   * @std_list: Standards list @@ -106,12 +109,20 @@ struct tvp514x_decoder {  	int ver;  	int streaming; +	struct v4l2_pix_format pix; +	int num_fmts; +	const struct v4l2_fmtdesc *fmt_list; +  	enum tvp514x_std current_std;  	int num_stds;  	const struct tvp514x_std_info *std_list;  	/* Input and Output Routing parameters */  	u32 input;  	u32 output; + +	/* mc related members */ +	struct media_pad pad; +	struct v4l2_mbus_framefmt format;  };  /* TVP514x default register values */ @@ -200,6 +211,21 @@ static struct tvp514x_reg tvp514x_reg_list_default[] = {  };  /** + * List of image formats supported by TVP5146/47 decoder + * Currently we are using 8 bit mode only, but can be + * extended to 10/20 bit mode. + */ +static const struct v4l2_fmtdesc tvp514x_fmt_list[] = { +	{ +	 .index		= 0, +	 .type		= V4L2_BUF_TYPE_VIDEO_CAPTURE, +	 .flags		= 0, +	 .description	= "8-bit UYVY 4:2:2 Format", +	 .pixelformat	= V4L2_PIX_FMT_UYVY, +	}, +}; + +/**   * Supported standards -   *   * Currently supports two standards only, need to add support for rest of the @@ -733,7 +759,7 @@ tvp514x_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,  }  /** - * tvp514x_mbus_fmt_cap() - V4L2 decoder interface handler for try/s/g_mbus_fmt + * tvp514x_mbus_fmt() - V4L2 decoder interface handler for try/s/g_mbus_fmt   * @sd: pointer to standard V4L2 sub-device structure   * @f: pointer to the mediabus format structure   * @@ -751,12 +777,11 @@ tvp514x_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f)  	/* Calculate height and width based on current standard */  	current_std = decoder->current_std; -	f->code = V4L2_MBUS_FMT_YUYV10_2X10; +	f->code = V4L2_MBUS_FMT_YUYV8_2X8;  	f->width = decoder->std_list[current_std].width;  	f->height = decoder->std_list[current_std].height;  	f->field = V4L2_FIELD_INTERLACED;  	f->colorspace = V4L2_COLORSPACE_SMPTE170M; -  	v4l2_dbg(1, debug, sd, "MBUS_FMT: Width - %d, Height - %d\n",  			f->width, f->height);  	return 0; @@ -892,6 +917,88 @@ static const struct v4l2_ctrl_ops tvp514x_ctrl_ops = {  	.s_ctrl = tvp514x_s_ctrl,  }; +/** + * tvp514x_enum_mbus_code() - V4L2 decoder interface handler for enum_mbus_code + * @sd: pointer to standard V4L2 sub-device structure + * @fh: file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + * + * Enumertaes mbus codes supported + */ +static int tvp514x_enum_mbus_code(struct v4l2_subdev *sd, +				  struct v4l2_subdev_fh *fh, +				  struct v4l2_subdev_mbus_code_enum *code) +{ +	u32 pad = code->pad; +	u32 index = code->index; + +	memset(code, 0, sizeof(*code)); +	code->index = index; +	code->pad = pad; + +	if (index != 0) +		return -EINVAL; + +	code->code = V4L2_MBUS_FMT_YUYV8_2X8; + +	return 0; +} + +/** + * tvp514x_get_pad_format() - V4L2 decoder interface handler for get pad format + * @sd: pointer to standard V4L2 sub-device structure + * @fh: file handle + * @format: pointer to v4l2_subdev_format structure + * + * Retrieves pad format which is active or tried based on requirement + */ +static int tvp514x_get_pad_format(struct v4l2_subdev *sd, +				  struct v4l2_subdev_fh *fh, +				  struct v4l2_subdev_format *format) +{ +	struct tvp514x_decoder *decoder = to_decoder(sd); +	__u32 which = format->which; + +	if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { +		format->format = decoder->format; +		return 0; +	} + +	format->format.code = V4L2_MBUS_FMT_YUYV8_2X8; +	format->format.width = tvp514x_std_list[decoder->current_std].width; +	format->format.height = tvp514x_std_list[decoder->current_std].height; +	format->format.colorspace = V4L2_COLORSPACE_SMPTE170M; +	format->format.field = V4L2_FIELD_INTERLACED; + +	return 0; +} + +/** + * tvp514x_set_pad_format() - V4L2 decoder interface handler for set pad format + * @sd: pointer to standard V4L2 sub-device structure + * @fh: file handle + * @format: pointer to v4l2_subdev_format structure + * + * Set pad format for the output pad + */ +static int tvp514x_set_pad_format(struct v4l2_subdev *sd, +				  struct v4l2_subdev_fh *fh, +				  struct v4l2_subdev_format *fmt) +{ +	struct tvp514x_decoder *decoder = to_decoder(sd); + +	if (fmt->format.field != V4L2_FIELD_INTERLACED || +	    fmt->format.code != V4L2_MBUS_FMT_YUYV8_2X8 || +	    fmt->format.colorspace != V4L2_COLORSPACE_SMPTE170M || +	    fmt->format.width != tvp514x_std_list[decoder->current_std].width || +	    fmt->format.height != tvp514x_std_list[decoder->current_std].height) +		return -EINVAL; + +	decoder->format = fmt->format; + +	return 0; +} +  static const struct v4l2_subdev_core_ops tvp514x_core_ops = {  	.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,  	.try_ext_ctrls = v4l2_subdev_try_ext_ctrls, @@ -915,13 +1022,33 @@ static const struct v4l2_subdev_video_ops tvp514x_video_ops = {  	.s_stream = tvp514x_s_stream,  }; +static const struct v4l2_subdev_pad_ops tvp514x_pad_ops = { +	.enum_mbus_code = tvp514x_enum_mbus_code, +	.get_fmt = tvp514x_get_pad_format, +	.set_fmt = tvp514x_set_pad_format, +}; +  static const struct v4l2_subdev_ops tvp514x_ops = {  	.core = &tvp514x_core_ops,  	.video = &tvp514x_video_ops, +	.pad = &tvp514x_pad_ops,  };  static struct tvp514x_decoder tvp514x_dev = {  	.streaming = 0, +	.fmt_list = tvp514x_fmt_list, +	.num_fmts = ARRAY_SIZE(tvp514x_fmt_list), +	.pix = { +		/* Default to NTSC 8-bit YUV 422 */ +		.width		= NTSC_NUM_ACTIVE_PIXELS, +		.height		= NTSC_NUM_ACTIVE_LINES, +		.pixelformat	= V4L2_PIX_FMT_UYVY, +		.field		= V4L2_FIELD_INTERLACED, +		.bytesperline	= NTSC_NUM_ACTIVE_PIXELS * 2, +		.sizeimage	= NTSC_NUM_ACTIVE_PIXELS * 2 * +					NTSC_NUM_ACTIVE_LINES, +		.colorspace	= V4L2_COLORSPACE_SMPTE170M, +		},  	.current_std = STD_NTSC_MJ,  	.std_list = tvp514x_std_list,  	.num_stds = ARRAY_SIZE(tvp514x_std_list), @@ -941,6 +1068,7 @@ tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id)  {  	struct tvp514x_decoder *decoder;  	struct v4l2_subdev *sd; +	int ret;  	/* Check if the adapter supports the needed features */  	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -981,7 +1109,21 @@ tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id)  	/* Register with V4L2 layer as slave device */  	sd = &decoder->sd;  	v4l2_i2c_subdev_init(sd, client, &tvp514x_ops); +	strlcpy(sd->name, TVP514X_MODULE_NAME, sizeof(sd->name)); +#if defined(CONFIG_MEDIA_CONTROLLER) +	decoder->pad.flags = MEDIA_PAD_FL_SOURCE; +	decoder->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; +	decoder->sd.entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER; + +	ret = media_entity_init(&decoder->sd.entity, 1, &decoder->pad, 0); +	if (ret < 0) { +		v4l2_err(sd, "%s decoder driver failed to register !!\n", +			 sd->name); +		kfree(decoder); +		return ret; +	} +#endif  	v4l2_ctrl_handler_init(&decoder->hdl, 5);  	v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops,  		V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); @@ -995,10 +1137,10 @@ tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id)  		V4L2_CID_AUTOGAIN, 0, 1, 1, 1);  	sd->ctrl_handler = &decoder->hdl;  	if (decoder->hdl.error) { -		int err = decoder->hdl.error; +		ret = decoder->hdl.error;  		v4l2_ctrl_handler_free(&decoder->hdl); -		return err; +		return ret;  	}  	v4l2_ctrl_handler_setup(&decoder->hdl); @@ -1021,6 +1163,9 @@ static int tvp514x_remove(struct i2c_client *client)  	struct tvp514x_decoder *decoder = to_decoder(sd);  	v4l2_device_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) +	media_entity_cleanup(&decoder->sd.entity); +#endif  	v4l2_ctrl_handler_free(&decoder->hdl);  	return 0;  } diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 5967e1a0c80..485159a3c0b 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -1067,7 +1067,7 @@ static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *  	return 0;  } -static int tvp5150_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int tvp5150_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c index 537f6b4d491..027809cca5f 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -326,9 +326,8 @@ static const struct i2c_reg_value tvp7002_parms_720P50[] = {  	{ TVP7002_EOR, 0xff, TVP7002_RESERVED }  }; -/* Preset definition for handling device operation */ -struct tvp7002_preset_definition { -	u32 preset; +/* Timings definition for handling device operation */ +struct tvp7002_timings_definition {  	struct v4l2_dv_timings timings;  	const struct i2c_reg_value *p_settings;  	enum v4l2_colorspace color_space; @@ -339,10 +338,9 @@ struct tvp7002_preset_definition {  	u16 cpl_max;  }; -/* Struct list for digital video presets */ -static const struct tvp7002_preset_definition tvp7002_presets[] = { +/* Struct list for digital video timings */ +static const struct tvp7002_timings_definition tvp7002_timings[] = {  	{ -		V4L2_DV_720P60,  		V4L2_DV_BT_CEA_1280X720P60,  		tvp7002_parms_720P60,  		V4L2_COLORSPACE_REC709, @@ -353,7 +351,6 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = {  		153  	},  	{ -		V4L2_DV_1080I60,  		V4L2_DV_BT_CEA_1920X1080I60,  		tvp7002_parms_1080I60,  		V4L2_COLORSPACE_REC709, @@ -364,7 +361,6 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = {  		205  	},  	{ -		V4L2_DV_1080I50,  		V4L2_DV_BT_CEA_1920X1080I50,  		tvp7002_parms_1080I50,  		V4L2_COLORSPACE_REC709, @@ -375,7 +371,6 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = {  		245  	},  	{ -		V4L2_DV_720P50,  		V4L2_DV_BT_CEA_1280X720P50,  		tvp7002_parms_720P50,  		V4L2_COLORSPACE_REC709, @@ -386,7 +381,6 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = {  		183  	},  	{ -		V4L2_DV_1080P60,  		V4L2_DV_BT_CEA_1920X1080P60,  		tvp7002_parms_1080P60,  		V4L2_COLORSPACE_REC709, @@ -397,7 +391,6 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = {  		102  	},  	{ -		V4L2_DV_480P59_94,  		V4L2_DV_BT_CEA_720X480P59_94,  		tvp7002_parms_480P,  		V4L2_COLORSPACE_SMPTE170M, @@ -408,7 +401,6 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = {  		0xffff  	},  	{ -		V4L2_DV_576P50,  		V4L2_DV_BT_CEA_720X576P50,  		tvp7002_parms_576P,  		V4L2_COLORSPACE_SMPTE170M, @@ -420,7 +412,7 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = {  	}  }; -#define NUM_PRESETS	ARRAY_SIZE(tvp7002_presets) +#define NUM_TIMINGS ARRAY_SIZE(tvp7002_timings)  /* Device definition */  struct tvp7002 { @@ -431,7 +423,7 @@ struct tvp7002 {  	int ver;  	int streaming; -	const struct tvp7002_preset_definition *current_preset; +	const struct tvp7002_timings_definition *current_timings;  };  /* @@ -588,32 +580,6 @@ static int tvp7002_write_inittab(struct v4l2_subdev *sd,  	return error;  } -/* - * tvp7002_s_dv_preset() - Set digital video preset - * @sd: ptr to v4l2_subdev struct - * @dv_preset: ptr to v4l2_dv_preset struct - * - * Set the digital video preset for a TVP7002 decoder device. - * Returns zero when successful or -EINVAL if register access fails. - */ -static int tvp7002_s_dv_preset(struct v4l2_subdev *sd, -					struct v4l2_dv_preset *dv_preset) -{ -	struct tvp7002 *device = to_tvp7002(sd); -	u32 preset; -	int i; - -	for (i = 0; i < NUM_PRESETS; i++) { -		preset = tvp7002_presets[i].preset; -		if (preset == dv_preset->preset) { -			device->current_preset = &tvp7002_presets[i]; -			return tvp7002_write_inittab(sd, tvp7002_presets[i].p_settings); -		} -	} - -	return -EINVAL; -} -  static int tvp7002_s_dv_timings(struct v4l2_subdev *sd,  					struct v4l2_dv_timings *dv_timings)  { @@ -623,12 +589,12 @@ static int tvp7002_s_dv_timings(struct v4l2_subdev *sd,  	if (dv_timings->type != V4L2_DV_BT_656_1120)  		return -EINVAL; -	for (i = 0; i < NUM_PRESETS; i++) { -		const struct v4l2_bt_timings *t = &tvp7002_presets[i].timings.bt; +	for (i = 0; i < NUM_TIMINGS; i++) { +		const struct v4l2_bt_timings *t = &tvp7002_timings[i].timings.bt;  		if (!memcmp(bt, t, &bt->standards - &bt->width)) { -			device->current_preset = &tvp7002_presets[i]; -			return tvp7002_write_inittab(sd, tvp7002_presets[i].p_settings); +			device->current_timings = &tvp7002_timings[i]; +			return tvp7002_write_inittab(sd, tvp7002_timings[i].p_settings);  		}  	}  	return -EINVAL; @@ -639,7 +605,7 @@ static int tvp7002_g_dv_timings(struct v4l2_subdev *sd,  {  	struct tvp7002 *device = to_tvp7002(sd); -	*dv_timings = device->current_preset->timings; +	*dv_timings = device->current_timings->timings;  	return 0;  } @@ -677,19 +643,13 @@ static int tvp7002_s_ctrl(struct v4l2_ctrl *ctrl)  static int tvp7002_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f)  {  	struct tvp7002 *device = to_tvp7002(sd); -	struct v4l2_dv_enum_preset e_preset; -	int error; +	const struct v4l2_bt_timings *bt = &device->current_timings->timings.bt; -	/* Calculate height and width based on current standard */ -	error = v4l_fill_dv_preset_info(device->current_preset->preset, &e_preset); -	if (error) -		return error; - -	f->width = e_preset.width; -	f->height = e_preset.height; +	f->width = bt->width; +	f->height = bt->height;  	f->code = V4L2_MBUS_FMT_YUYV10_1X20; -	f->field = device->current_preset->scanmode; -	f->colorspace = device->current_preset->color_space; +	f->field = device->current_timings->scanmode; +	f->colorspace = device->current_timings->color_space;  	v4l2_dbg(1, debug, sd, "MBUS_FMT: Width - %d, Height - %d",  			f->width, f->height); @@ -697,16 +657,16 @@ static int tvp7002_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f  }  /* - * tvp7002_query_dv_preset() - query DV preset + * tvp7002_query_dv() - query DV timings   * @sd: pointer to standard V4L2 sub-device structure - * @qpreset: standard V4L2 v4l2_dv_preset structure + * @index: index into the tvp7002_timings array   * - * Returns the current DV preset by TVP7002. If no active input is + * Returns the current DV timings detected by TVP7002. If no active input is   * detected, returns -EINVAL   */  static int tvp7002_query_dv(struct v4l2_subdev *sd, int *index)  { -	const struct tvp7002_preset_definition *presets = tvp7002_presets; +	const struct tvp7002_timings_definition *timings = tvp7002_timings;  	u8 progressive;  	u32 lpfr;  	u32 cpln; @@ -717,7 +677,7 @@ static int tvp7002_query_dv(struct v4l2_subdev *sd, int *index)  	u8 cpl_msb;  	/* Return invalid index if no active input is detected */ -	*index = NUM_PRESETS; +	*index = NUM_TIMINGS;  	/* Read standards from device registers */  	tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_LSBS, &lpf_lsb, &error); @@ -738,39 +698,23 @@ static int tvp7002_query_dv(struct v4l2_subdev *sd, int *index)  	progressive = (lpf_msb & TVP7002_INPR_MASK) >> TVP7002_IP_SHIFT;  	/* Do checking of video modes */ -	for (*index = 0; *index < NUM_PRESETS; (*index)++, presets++) -		if (lpfr == presets->lines_per_frame && -			progressive == presets->progressive) { -			if (presets->cpl_min == 0xffff) +	for (*index = 0; *index < NUM_TIMINGS; (*index)++, timings++) +		if (lpfr == timings->lines_per_frame && +			progressive == timings->progressive) { +			if (timings->cpl_min == 0xffff)  				break; -			if (cpln >= presets->cpl_min && cpln <= presets->cpl_max) +			if (cpln >= timings->cpl_min && cpln <= timings->cpl_max)  				break;  		} -	if (*index == NUM_PRESETS) { +	if (*index == NUM_TIMINGS) {  		v4l2_dbg(1, debug, sd, "detection failed: lpf = %x, cpl = %x\n",  								lpfr, cpln);  		return -ENOLINK;  	}  	/* Update lines per frame and clocks per line info */ -	v4l2_dbg(1, debug, sd, "detected preset: %d\n", *index); -	return 0; -} - -static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, -					struct v4l2_dv_preset *qpreset) -{ -	int index; -	int err = tvp7002_query_dv(sd, &index); - -	if (err || index == NUM_PRESETS) { -		qpreset->preset = V4L2_DV_INVALID; -		if (err == -ENOLINK) -			err = 0; -		return err; -	} -	qpreset->preset = tvp7002_presets[index].preset; +	v4l2_dbg(1, debug, sd, "detected timings: %d\n", *index);  	return 0;  } @@ -782,7 +726,7 @@ static int tvp7002_query_dv_timings(struct v4l2_subdev *sd,  	if (err)  		return err; -	*timings = tvp7002_presets[index].timings; +	*timings = tvp7002_timings[index].timings;  	return 0;  } @@ -824,7 +768,7 @@ static int tvp7002_g_register(struct v4l2_subdev *sd,   * -EPERM if call not allowed.   */  static int tvp7002_s_register(struct v4l2_subdev *sd, -						struct v4l2_dbg_register *reg) +						const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -896,35 +840,21 @@ static int tvp7002_s_stream(struct v4l2_subdev *sd, int enable)   */  static int tvp7002_log_status(struct v4l2_subdev *sd)  { -	const struct tvp7002_preset_definition *presets = tvp7002_presets;  	struct tvp7002 *device = to_tvp7002(sd); -	struct v4l2_dv_enum_preset e_preset; -	struct v4l2_dv_preset detected; -	int i; - -	detected.preset = V4L2_DV_INVALID; -	/* Find my current standard*/ -	tvp7002_query_dv_preset(sd, &detected); +	const struct v4l2_bt_timings *bt; +	int detected; -	/* Print standard related code values */ -	for (i = 0; i < NUM_PRESETS; i++, presets++) -		if (presets->preset == detected.preset) -			break; +	/* Find my current timings */ +	tvp7002_query_dv(sd, &detected); -	if (v4l_fill_dv_preset_info(device->current_preset->preset, &e_preset)) -		return -EINVAL; - -	v4l2_info(sd, "Selected DV Preset: %s\n", e_preset.name); -	v4l2_info(sd, "   Pixels per line: %u\n", e_preset.width); -	v4l2_info(sd, "   Lines per frame: %u\n\n", e_preset.height); -	if (i == NUM_PRESETS) { -		v4l2_info(sd, "Detected DV Preset: None\n"); +	bt = &device->current_timings->timings.bt; +	v4l2_info(sd, "Selected DV Timings: %ux%u\n", bt->width, bt->height); +	if (detected == NUM_TIMINGS) { +		v4l2_info(sd, "Detected DV Timings: None\n");  	} else { -		if (v4l_fill_dv_preset_info(presets->preset, &e_preset)) -			return -EINVAL; -		v4l2_info(sd, "Detected DV Preset: %s\n", e_preset.name); -		v4l2_info(sd, "  Pixels per line: %u\n", e_preset.width); -		v4l2_info(sd, "  Lines per frame: %u\n\n", e_preset.height); +		bt = &tvp7002_timings[detected].timings.bt; +		v4l2_info(sd, "Detected DV Timings: %ux%u\n", +				bt->width, bt->height);  	}  	v4l2_info(sd, "Streaming enabled: %s\n",  					device->streaming ? "yes" : "no"); @@ -935,31 +865,14 @@ static int tvp7002_log_status(struct v4l2_subdev *sd)  	return 0;  } -/* - * tvp7002_enum_dv_presets() - Enum supported digital video formats - * @sd: pointer to standard V4L2 sub-device structure - * @preset: pointer to format struct - * - * Enumerate supported digital video formats. - */ -static int tvp7002_enum_dv_presets(struct v4l2_subdev *sd, -		struct v4l2_dv_enum_preset *preset) -{ -	/* Check requested format index is within range */ -	if (preset->index >= NUM_PRESETS) -		return -EINVAL; - -	return v4l_fill_dv_preset_info(tvp7002_presets[preset->index].preset, preset); -} -  static int tvp7002_enum_dv_timings(struct v4l2_subdev *sd,  		struct v4l2_enum_dv_timings *timings)  {  	/* Check requested format index is within range */ -	if (timings->index >= NUM_PRESETS) +	if (timings->index >= NUM_TIMINGS)  		return -EINVAL; -	timings->timings = tvp7002_presets[timings->index].timings; +	timings->timings = tvp7002_timings[timings->index].timings;  	return 0;  } @@ -986,9 +899,6 @@ static const struct v4l2_subdev_core_ops tvp7002_core_ops = {  /* Specific video subsystem operation handlers */  static const struct v4l2_subdev_video_ops tvp7002_video_ops = { -	.enum_dv_presets = tvp7002_enum_dv_presets, -	.s_dv_preset = tvp7002_s_dv_preset, -	.query_dv_preset = tvp7002_query_dv_preset,  	.g_dv_timings = tvp7002_g_dv_timings,  	.s_dv_timings = tvp7002_s_dv_timings,  	.enum_dv_timings = tvp7002_enum_dv_timings, @@ -1019,7 +929,7 @@ static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id)  {  	struct v4l2_subdev *sd;  	struct tvp7002 *device; -	struct v4l2_dv_preset preset; +	struct v4l2_dv_timings timings;  	int polarity_a;  	int polarity_b;  	u8 revision; @@ -1043,7 +953,7 @@ static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id)  	sd = &device->sd;  	device->pdata = c->dev.platform_data; -	device->current_preset = tvp7002_presets; +	device->current_timings = tvp7002_timings;  	/* Tell v4l2 the device is ready */  	v4l2_i2c_subdev_init(sd, c, &tvp7002_ops); @@ -1080,8 +990,8 @@ static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id)  		return error;  	/* Set registers according to default video mode */ -	preset.preset = device->current_preset->preset; -	error = tvp7002_s_dv_preset(sd, &preset); +	timings = device->current_timings->timings; +	error = tvp7002_s_dv_timings(sd, &timings);  	v4l2_ctrl_handler_init(&device->hdl, 1);  	v4l2_ctrl_new_std(&device->hdl, &tvp7002_ctrl_ops, diff --git a/drivers/media/i2c/tw2804.c b/drivers/media/i2c/tw2804.c new file mode 100644 index 00000000000..c5dc2c3bf2d --- /dev/null +++ b/drivers/media/i2c/tw2804.c @@ -0,0 +1,453 @@ +/* + * Copyright (C) 2005-2006 Micronas USA Inc. + * + * 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <linux/ioctl.h> +#include <linux/slab.h> +#include <media/v4l2-subdev.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-ctrls.h> + +#define TW2804_REG_AUTOGAIN		0x02 +#define TW2804_REG_HUE			0x0f +#define TW2804_REG_SATURATION		0x10 +#define TW2804_REG_CONTRAST		0x11 +#define TW2804_REG_BRIGHTNESS		0x12 +#define TW2804_REG_COLOR_KILLER		0x14 +#define TW2804_REG_GAIN			0x3c +#define TW2804_REG_CHROMA_GAIN		0x3d +#define TW2804_REG_BLUE_BALANCE		0x3e +#define TW2804_REG_RED_BALANCE		0x3f + +struct tw2804 { +	struct v4l2_subdev sd; +	struct v4l2_ctrl_handler hdl; +	u8 channel:2; +	u8 input:1; +	int norm; +}; + +static const u8 global_registers[] = { +	0x39, 0x00, +	0x3a, 0xff, +	0x3b, 0x84, +	0x3c, 0x80, +	0x3d, 0x80, +	0x3e, 0x82, +	0x3f, 0x82, +	0x78, 0x00, +	0xff, 0xff, /* Terminator (reg 0xff does not exist) */ +}; + +static const u8 channel_registers[] = { +	0x01, 0xc4, +	0x02, 0xa5, +	0x03, 0x20, +	0x04, 0xd0, +	0x05, 0x20, +	0x06, 0xd0, +	0x07, 0x88, +	0x08, 0x20, +	0x09, 0x07, +	0x0a, 0xf0, +	0x0b, 0x07, +	0x0c, 0xf0, +	0x0d, 0x40, +	0x0e, 0xd2, +	0x0f, 0x80, +	0x10, 0x80, +	0x11, 0x80, +	0x12, 0x80, +	0x13, 0x1f, +	0x14, 0x00, +	0x15, 0x00, +	0x16, 0x00, +	0x17, 0x00, +	0x18, 0xff, +	0x19, 0xff, +	0x1a, 0xff, +	0x1b, 0xff, +	0x1c, 0xff, +	0x1d, 0xff, +	0x1e, 0xff, +	0x1f, 0xff, +	0x20, 0x07, +	0x21, 0x07, +	0x22, 0x00, +	0x23, 0x91, +	0x24, 0x51, +	0x25, 0x03, +	0x26, 0x00, +	0x27, 0x00, +	0x28, 0x00, +	0x29, 0x00, +	0x2a, 0x00, +	0x2b, 0x00, +	0x2c, 0x00, +	0x2d, 0x00, +	0x2e, 0x00, +	0x2f, 0x00, +	0x30, 0x00, +	0x31, 0x00, +	0x32, 0x00, +	0x33, 0x00, +	0x34, 0x00, +	0x35, 0x00, +	0x36, 0x00, +	0x37, 0x00, +	0xff, 0xff, /* Terminator (reg 0xff does not exist) */ +}; + +static int write_reg(struct i2c_client *client, u8 reg, u8 value, u8 channel) +{ +	return i2c_smbus_write_byte_data(client, reg | (channel << 6), value); +} + +static int write_regs(struct i2c_client *client, const u8 *regs, u8 channel) +{ +	int ret; +	int i; + +	for (i = 0; regs[i] != 0xff; i += 2) { +		ret = i2c_smbus_write_byte_data(client, +				regs[i] | (channel << 6), regs[i + 1]); +		if (ret < 0) +			return ret; +	} +	return 0; +} + +static int read_reg(struct i2c_client *client, u8 reg, u8 channel) +{ +	return i2c_smbus_read_byte_data(client, (reg) | (channel << 6)); +} + +static inline struct tw2804 *to_state(struct v4l2_subdev *sd) +{ +	return container_of(sd, struct tw2804, sd); +} + +static inline struct tw2804 *to_state_from_ctrl(struct v4l2_ctrl *ctrl) +{ +	return container_of(ctrl->handler, struct tw2804, hdl); +} + +static int tw2804_log_status(struct v4l2_subdev *sd) +{ +	struct tw2804 *state = to_state(sd); + +	v4l2_info(sd, "Standard: %s\n", +			state->norm & V4L2_STD_525_60 ? "60 Hz" : "50 Hz"); +	v4l2_info(sd, "Channel: %d\n", state->channel); +	v4l2_info(sd, "Input: %d\n", state->input); +	return v4l2_ctrl_subdev_log_status(sd); +} + +/* + * These volatile controls are needed because all four channels share + * these controls. So a change made to them through one channel would + * require another channel to be updated. + * + * Normally this would have been done in a different way, but since the one + * board that uses this driver sees this single chip as if it was on four + * different i2c adapters (each adapter belonging to a separate instance of + * the same USB driver) there is no reliable method that I have found to let + * the instances know about each other. + * + * So implementing these global registers as volatile is the best we can do. + */ +static int tw2804_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ +	struct tw2804 *state = to_state_from_ctrl(ctrl); +	struct i2c_client *client = v4l2_get_subdevdata(&state->sd); + +	switch (ctrl->id) { +	case V4L2_CID_GAIN: +		ctrl->val = read_reg(client, TW2804_REG_GAIN, 0); +		return 0; + +	case V4L2_CID_CHROMA_GAIN: +		ctrl->val = read_reg(client, TW2804_REG_CHROMA_GAIN, 0); +		return 0; + +	case V4L2_CID_BLUE_BALANCE: +		ctrl->val = read_reg(client, TW2804_REG_BLUE_BALANCE, 0); +		return 0; + +	case V4L2_CID_RED_BALANCE: +		ctrl->val = read_reg(client, TW2804_REG_RED_BALANCE, 0); +		return 0; +	} +	return 0; +} + +static int tw2804_s_ctrl(struct v4l2_ctrl *ctrl) +{ +	struct tw2804 *state = to_state_from_ctrl(ctrl); +	struct i2c_client *client = v4l2_get_subdevdata(&state->sd); +	int addr; +	int reg; + +	switch (ctrl->id) { +	case V4L2_CID_AUTOGAIN: +		addr = TW2804_REG_AUTOGAIN; +		reg = read_reg(client, addr, state->channel); +		if (reg < 0) +			return reg; +		if (ctrl->val == 0) +			reg &= ~(1 << 7); +		else +			reg |= 1 << 7; +		return write_reg(client, addr, reg, state->channel); + +	case V4L2_CID_COLOR_KILLER: +		addr = TW2804_REG_COLOR_KILLER; +		reg = read_reg(client, addr, state->channel); +		if (reg < 0) +			return reg; +		reg = (reg & ~(0x03)) | (ctrl->val == 0 ? 0x02 : 0x03); +		return write_reg(client, addr, reg, state->channel); + +	case V4L2_CID_GAIN: +		return write_reg(client, TW2804_REG_GAIN, ctrl->val, 0); + +	case V4L2_CID_CHROMA_GAIN: +		return write_reg(client, TW2804_REG_CHROMA_GAIN, ctrl->val, 0); + +	case V4L2_CID_BLUE_BALANCE: +		return write_reg(client, TW2804_REG_BLUE_BALANCE, ctrl->val, 0); + +	case V4L2_CID_RED_BALANCE: +		return write_reg(client, TW2804_REG_RED_BALANCE, ctrl->val, 0); + +	case V4L2_CID_BRIGHTNESS: +		return write_reg(client, TW2804_REG_BRIGHTNESS, +				ctrl->val, state->channel); + +	case V4L2_CID_CONTRAST: +		return write_reg(client, TW2804_REG_CONTRAST, +				ctrl->val, state->channel); + +	case V4L2_CID_SATURATION: +		return write_reg(client, TW2804_REG_SATURATION, +				ctrl->val, state->channel); + +	case V4L2_CID_HUE: +		return write_reg(client, TW2804_REG_HUE, +				ctrl->val, state->channel); + +	default: +		break; +	} +	return -EINVAL; +} + +static int tw2804_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ +	struct tw2804 *dec = to_state(sd); +	struct i2c_client *client = v4l2_get_subdevdata(sd); +	bool is_60hz = norm & V4L2_STD_525_60; +	u8 regs[] = { +		0x01, is_60hz ? 0xc4 : 0x84, +		0x09, is_60hz ? 0x07 : 0x04, +		0x0a, is_60hz ? 0xf0 : 0x20, +		0x0b, is_60hz ? 0x07 : 0x04, +		0x0c, is_60hz ? 0xf0 : 0x20, +		0x0d, is_60hz ? 0x40 : 0x4a, +		0x16, is_60hz ? 0x00 : 0x40, +		0x17, is_60hz ? 0x00 : 0x40, +		0x20, is_60hz ? 0x07 : 0x0f, +		0x21, is_60hz ? 0x07 : 0x0f, +		0xff, 0xff, +	}; + +	write_regs(client, regs, dec->channel); +	dec->norm = norm; +	return 0; +} + +static int tw2804_s_video_routing(struct v4l2_subdev *sd, u32 input, u32 output, +	u32 config) +{ +	struct tw2804 *dec = to_state(sd); +	struct i2c_client *client = v4l2_get_subdevdata(sd); +	int reg; + +	if (config && config - 1 != dec->channel) { +		if (config > 4) { +			dev_err(&client->dev, +				"channel %d is not between 1 and 4!\n", config); +			return -EINVAL; +		} +		dec->channel = config - 1; +		dev_dbg(&client->dev, "initializing TW2804 channel %d\n", +			dec->channel); +		if (dec->channel == 0 && +				write_regs(client, global_registers, 0) < 0) { +			dev_err(&client->dev, +				"error initializing TW2804 global registers\n"); +			return -EIO; +		} +		if (write_regs(client, channel_registers, dec->channel) < 0) { +			dev_err(&client->dev, +				"error initializing TW2804 channel %d\n", +				dec->channel); +			return -EIO; +		} +	} + +	if (input > 1) +		return -EINVAL; + +	if (input == dec->input) +		return 0; + +	reg = read_reg(client, 0x22, dec->channel); + +	if (reg >= 0) { +		if (input == 0) +			reg &= ~(1 << 2); +		else +			reg |= 1 << 2; +		reg = write_reg(client, 0x22, reg, dec->channel); +	} + +	if (reg >= 0) +		dec->input = input; +	else +		return reg; +	return 0; +} + +static const struct v4l2_ctrl_ops tw2804_ctrl_ops = { +	.g_volatile_ctrl = tw2804_g_volatile_ctrl, +	.s_ctrl = tw2804_s_ctrl, +}; + +static const struct v4l2_subdev_video_ops tw2804_video_ops = { +	.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 = { +	.core = &tw2804_core_ops, +	.video = &tw2804_video_ops, +}; + +static int tw2804_probe(struct i2c_client *client, +			    const struct i2c_device_id *id) +{ +	struct i2c_adapter *adapter = client->adapter; +	struct tw2804 *state; +	struct v4l2_subdev *sd; +	struct v4l2_ctrl *ctrl; +	int err; + +	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) +		return -ENODEV; + +	state = kzalloc(sizeof(struct tw2804), GFP_KERNEL); + +	if (state == NULL) +		return -ENOMEM; +	sd = &state->sd; +	v4l2_i2c_subdev_init(sd, client, &tw2804_ops); +	state->channel = -1; +	state->norm = V4L2_STD_NTSC; + +	v4l2_ctrl_handler_init(&state->hdl, 10); +	v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, +				V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); +	v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, +				V4L2_CID_CONTRAST, 0, 255, 1, 128); +	v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, +				V4L2_CID_SATURATION, 0, 255, 1, 128); +	v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, +				V4L2_CID_HUE, 0, 255, 1, 128); +	v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, +				V4L2_CID_COLOR_KILLER, 0, 1, 1, 0); +	v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, +				V4L2_CID_AUTOGAIN, 0, 1, 1, 0); +	ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, +				V4L2_CID_GAIN, 0, 255, 1, 128); +	if (ctrl) +		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; +	ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, +				V4L2_CID_CHROMA_GAIN, 0, 255, 1, 128); +	if (ctrl) +		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; +	ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, +				V4L2_CID_BLUE_BALANCE, 0, 255, 1, 122); +	if (ctrl) +		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; +	ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, +				V4L2_CID_RED_BALANCE, 0, 255, 1, 122); +	if (ctrl) +		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; +	sd->ctrl_handler = &state->hdl; +	err = state->hdl.error; +	if (err) { +		v4l2_ctrl_handler_free(&state->hdl); +		kfree(state); +		return err; +	} + +	v4l_info(client, "chip found @ 0x%02x (%s)\n", +			client->addr << 1, client->adapter->name); + +	return 0; +} + +static int tw2804_remove(struct i2c_client *client) +{ +	struct v4l2_subdev *sd = i2c_get_clientdata(client); +	struct tw2804 *state = to_state(sd); + +	v4l2_device_unregister_subdev(sd); +	v4l2_ctrl_handler_free(&state->hdl); +	kfree(state); +	return 0; +} + +static const struct i2c_device_id tw2804_id[] = { +	{ "tw2804", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, tw2804_id); + +static struct i2c_driver tw2804_driver = { +	.driver = { +		.name	= "tw2804", +	}, +	.probe		= tw2804_probe, +	.remove		= tw2804_remove, +	.id_table	= tw2804_id, +}; + +module_i2c_driver(tw2804_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TW2804/TW2802 V4L2 i2c driver"); +MODULE_AUTHOR("Micronas USA Inc"); diff --git a/drivers/media/i2c/tw9903.c b/drivers/media/i2c/tw9903.c new file mode 100644 index 00000000000..87880b19d8c --- /dev/null +++ b/drivers/media/i2c/tw9903.c @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2005-2006 Micronas USA Inc. + * + * 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <linux/ioctl.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> +#include <linux/slab.h> + +MODULE_DESCRIPTION("TW9903 I2C subdev driver"); +MODULE_LICENSE("GPL v2"); + +/* + * This driver is based on the wis-tw9903.c source that was in + * drivers/staging/media/go7007. That source had commented out code for + * saturation and scaling (neither seemed to work). If anyone ever gets + * hardware to test this driver, then that code might be useful to look at. + * You need to get the kernel sources of, say, kernel 3.8 where that + * wis-tw9903 driver is still present. + */ + +struct tw9903 { +	struct v4l2_subdev sd; +	struct v4l2_ctrl_handler hdl; +	v4l2_std_id norm; +}; + +static inline struct tw9903 *to_state(struct v4l2_subdev *sd) +{ +	return container_of(sd, struct tw9903, sd); +} + +static const u8 initial_registers[] = { +	0x02, 0x44, /* input 1, composite */ +	0x03, 0x92, /* correct digital format */ +	0x04, 0x00, +	0x05, 0x80, /* or 0x00 for PAL */ +	0x06, 0x40, /* second internal current reference */ +	0x07, 0x02, /* window */ +	0x08, 0x14, /* window */ +	0x09, 0xf0, /* window */ +	0x0a, 0x81, /* window */ +	0x0b, 0xd0, /* window */ +	0x0c, 0x8c, +	0x0d, 0x00, /* scaling */ +	0x0e, 0x11, /* scaling */ +	0x0f, 0x00, /* scaling */ +	0x10, 0x00, /* brightness */ +	0x11, 0x60, /* contrast */ +	0x12, 0x01, /* sharpness */ +	0x13, 0x7f, /* U gain */ +	0x14, 0x5a, /* V gain */ +	0x15, 0x00, /* hue */ +	0x16, 0xc3, /* sharpness */ +	0x18, 0x00, +	0x19, 0x58, /* vbi */ +	0x1a, 0x80, +	0x1c, 0x0f, /* video norm */ +	0x1d, 0x7f, /* video norm */ +	0x20, 0xa0, /* clamping gain (working 0x50) */ +	0x21, 0x22, +	0x22, 0xf0, +	0x23, 0xfe, +	0x24, 0x3c, +	0x25, 0x38, +	0x26, 0x44, +	0x27, 0x20, +	0x28, 0x00, +	0x29, 0x15, +	0x2a, 0xa0, +	0x2b, 0x44, +	0x2c, 0x37, +	0x2d, 0x00, +	0x2e, 0xa5, /* burst PLL control (working: a9) */ +	0x2f, 0xe0, /* 0xea is blue test frame -- 0xe0 for normal */ +	0x31, 0x00, +	0x33, 0x22, +	0x34, 0x11, +	0x35, 0x35, +	0x3b, 0x05, +	0x06, 0xc0, /* reset device */ +	0x00, 0x00, /* Terminator (reg 0x00 is read-only) */ +}; + +static int write_reg(struct v4l2_subdev *sd, u8 reg, u8 value) +{ +	struct i2c_client *client = v4l2_get_subdevdata(sd); + +	return i2c_smbus_write_byte_data(client, reg, value); +} + +static int write_regs(struct v4l2_subdev *sd, const u8 *regs) +{ +	int i; + +	for (i = 0; regs[i] != 0x00; i += 2) +		if (write_reg(sd, regs[i], regs[i + 1]) < 0) +			return -1; +	return 0; +} + +static int tw9903_s_video_routing(struct v4l2_subdev *sd, u32 input, +				      u32 output, u32 config) +{ +	write_reg(sd, 0x02, 0x40 | (input << 1)); +	return 0; +} + +static int tw9903_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ +	struct tw9903 *dec = to_state(sd); +	bool is_60hz = norm & V4L2_STD_525_60; +	static const u8 config_60hz[] = { +		0x05, 0x80, +		0x07, 0x02, +		0x08, 0x14, +		0x09, 0xf0, +		0,    0, +	}; +	static const u8 config_50hz[] = { +		0x05, 0x00, +		0x07, 0x12, +		0x08, 0x18, +		0x09, 0x20, +		0,    0, +	}; + +	write_regs(sd, is_60hz ? config_60hz : config_50hz); +	dec->norm = norm; +	return 0; +} + + +static int tw9903_s_ctrl(struct v4l2_ctrl *ctrl) +{ +	struct tw9903 *dec = container_of(ctrl->handler, struct tw9903, hdl); +	struct v4l2_subdev *sd = &dec->sd; + +	switch (ctrl->id) { +	case V4L2_CID_BRIGHTNESS: +		write_reg(sd, 0x10, ctrl->val); +		break; +	case V4L2_CID_CONTRAST: +		write_reg(sd, 0x11, ctrl->val); +		break; +	case V4L2_CID_HUE: +		write_reg(sd, 0x15, ctrl->val); +		break; +	default: +		return -EINVAL; +	} +	return 0; +} + +static int tw9903_log_status(struct v4l2_subdev *sd) +{ +	struct tw9903 *dec = to_state(sd); +	bool is_60hz = dec->norm & V4L2_STD_525_60; + +	v4l2_info(sd, "Standard: %d Hz\n", is_60hz ? 60 : 50); +	v4l2_ctrl_subdev_log_status(sd); +	return 0; +} + +/* --------------------------------------------------------------------------*/ + +static const struct v4l2_ctrl_ops tw9903_ctrl_ops = { +	.s_ctrl = tw9903_s_ctrl, +}; + +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_routing = tw9903_s_video_routing, +}; + +static const struct v4l2_subdev_ops tw9903_ops = { +	.core = &tw9903_core_ops, +	.video = &tw9903_video_ops, +}; + +/* --------------------------------------------------------------------------*/ + +static int tw9903_probe(struct i2c_client *client, +			     const struct i2c_device_id *id) +{ +	struct tw9903 *dec; +	struct v4l2_subdev *sd; +	struct v4l2_ctrl_handler *hdl; + +	/* Check if the adapter supports the needed features */ +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) +		return -EIO; + +	v4l_info(client, "chip found @ 0x%02x (%s)\n", +			client->addr << 1, client->adapter->name); + +	dec = kzalloc(sizeof(struct tw9903), GFP_KERNEL); +	if (dec == NULL) +		return -ENOMEM; +	sd = &dec->sd; +	v4l2_i2c_subdev_init(sd, client, &tw9903_ops); +	hdl = &dec->hdl; +	v4l2_ctrl_handler_init(hdl, 4); +	v4l2_ctrl_new_std(hdl, &tw9903_ctrl_ops, +		V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); +	v4l2_ctrl_new_std(hdl, &tw9903_ctrl_ops, +		V4L2_CID_CONTRAST, 0, 255, 1, 0x60); +	v4l2_ctrl_new_std(hdl, &tw9903_ctrl_ops, +		V4L2_CID_HUE, -128, 127, 1, 0); +	sd->ctrl_handler = hdl; +	if (hdl->error) { +		int err = hdl->error; + +		v4l2_ctrl_handler_free(hdl); +		kfree(dec); +		return err; +	} + +	/* Initialize tw9903 */ +	dec->norm = V4L2_STD_NTSC; + +	if (write_regs(sd, initial_registers) < 0) { +		v4l2_err(client, "error initializing TW9903\n"); +		kfree(dec); +		return -EINVAL; +	} + +	return 0; +} + +static int tw9903_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 tw9903_id[] = { +	{ "tw9903", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, tw9903_id); + +static struct i2c_driver tw9903_driver = { +	.driver = { +		.owner	= THIS_MODULE, +		.name	= "tw9903", +	}, +	.probe = tw9903_probe, +	.remove = tw9903_remove, +	.id_table = tw9903_id, +}; +module_i2c_driver(tw9903_driver); diff --git a/drivers/media/i2c/tw9906.c b/drivers/media/i2c/tw9906.c new file mode 100644 index 00000000000..accd79e5a7f --- /dev/null +++ b/drivers/media/i2c/tw9906.c @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2005-2006 Micronas USA Inc. + * + * 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <linux/ioctl.h> +#include <linux/slab.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> + +MODULE_DESCRIPTION("TW9906 I2C subdev driver"); +MODULE_LICENSE("GPL v2"); + +struct tw9906 { +	struct v4l2_subdev sd; +	struct v4l2_ctrl_handler hdl; +	v4l2_std_id norm; +}; + +static inline struct tw9906 *to_state(struct v4l2_subdev *sd) +{ +	return container_of(sd, struct tw9906, sd); +} + +static const u8 initial_registers[] = { +	0x02, 0x40, /* input 0, composite */ +	0x03, 0xa2, /* correct digital format */ +	0x05, 0x81, /* or 0x01 for PAL */ +	0x07, 0x02, /* window */ +	0x08, 0x14, /* window */ +	0x09, 0xf0, /* window */ +	0x0a, 0x10, /* window */ +	0x0b, 0xd0, /* window */ +	0x0d, 0x00, /* scaling */ +	0x0e, 0x11, /* scaling */ +	0x0f, 0x00, /* scaling */ +	0x10, 0x00, /* brightness */ +	0x11, 0x60, /* contrast */ +	0x12, 0x11, /* sharpness */ +	0x13, 0x7e, /* U gain */ +	0x14, 0x7e, /* V gain */ +	0x15, 0x00, /* hue */ +	0x19, 0x57, /* vbi */ +	0x1a, 0x0f, +	0x1b, 0x40, +	0x29, 0x03, +	0x55, 0x00, +	0x6b, 0x26, +	0x6c, 0x36, +	0x6d, 0xf0, +	0x6e, 0x41, +	0x6f, 0x13, +	0xad, 0x70, +	0x00, 0x00, /* Terminator (reg 0x00 is read-only) */ +}; + +static int write_reg(struct v4l2_subdev *sd, u8 reg, u8 value) +{ +	struct i2c_client *client = v4l2_get_subdevdata(sd); + +	return i2c_smbus_write_byte_data(client, reg, value); +} + +static int write_regs(struct v4l2_subdev *sd, const u8 *regs) +{ +	int i; + +	for (i = 0; regs[i] != 0x00; i += 2) +		if (write_reg(sd, regs[i], regs[i + 1]) < 0) +			return -1; +	return 0; +} + +static int tw9906_s_video_routing(struct v4l2_subdev *sd, u32 input, +				      u32 output, u32 config) +{ +	write_reg(sd, 0x02, 0x40 | (input << 1)); +	return 0; +} + +static int tw9906_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ +	struct tw9906 *dec = to_state(sd); +	bool is_60hz = norm & V4L2_STD_525_60; +	static const u8 config_60hz[] = { +		0x05, 0x81, +		0x07, 0x02, +		0x08, 0x14, +		0x09, 0xf0, +		0,    0, +	}; +	static const u8 config_50hz[] = { +		0x05, 0x01, +		0x07, 0x12, +		0x08, 0x18, +		0x09, 0x20, +		0,    0, +	}; + +	write_regs(sd, is_60hz ? config_60hz : config_50hz); +	dec->norm = norm; +	return 0; +} + +static int tw9906_s_ctrl(struct v4l2_ctrl *ctrl) +{ +	struct tw9906 *dec = container_of(ctrl->handler, struct tw9906, hdl); +	struct v4l2_subdev *sd = &dec->sd; + +	switch (ctrl->id) { +	case V4L2_CID_BRIGHTNESS: +		write_reg(sd, 0x10, ctrl->val); +		break; +	case V4L2_CID_CONTRAST: +		write_reg(sd, 0x11, ctrl->val); +		break; +	case V4L2_CID_HUE: +		write_reg(sd, 0x15, ctrl->val); +		break; +	default: +		return -EINVAL; +	} +	return 0; +} + +static int tw9906_log_status(struct v4l2_subdev *sd) +{ +	struct tw9906 *dec = to_state(sd); +	bool is_60hz = dec->norm & V4L2_STD_525_60; + +	v4l2_info(sd, "Standard: %d Hz\n", is_60hz ? 60 : 50); +	v4l2_ctrl_subdev_log_status(sd); +	return 0; +} + +/* --------------------------------------------------------------------------*/ + +static const struct v4l2_ctrl_ops tw9906_ctrl_ops = { +	.s_ctrl = tw9906_s_ctrl, +}; + +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_routing = tw9906_s_video_routing, +}; + +static const struct v4l2_subdev_ops tw9906_ops = { +	.core = &tw9906_core_ops, +	.video = &tw9906_video_ops, +}; + +static int tw9906_probe(struct i2c_client *client, +			     const struct i2c_device_id *id) +{ +	struct tw9906 *dec; +	struct v4l2_subdev *sd; +	struct v4l2_ctrl_handler *hdl; + +	/* Check if the adapter supports the needed features */ +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) +		return -EIO; + +	v4l_info(client, "chip found @ 0x%02x (%s)\n", +			client->addr << 1, client->adapter->name); + +	dec = kzalloc(sizeof(struct tw9906), GFP_KERNEL); +	if (dec == NULL) +		return -ENOMEM; +	sd = &dec->sd; +	v4l2_i2c_subdev_init(sd, client, &tw9906_ops); +	hdl = &dec->hdl; +	v4l2_ctrl_handler_init(hdl, 4); +	v4l2_ctrl_new_std(hdl, &tw9906_ctrl_ops, +		V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); +	v4l2_ctrl_new_std(hdl, &tw9906_ctrl_ops, +		V4L2_CID_CONTRAST, 0, 255, 1, 0x60); +	v4l2_ctrl_new_std(hdl, &tw9906_ctrl_ops, +		V4L2_CID_HUE, -128, 127, 1, 0); +	sd->ctrl_handler = hdl; +	if (hdl->error) { +		int err = hdl->error; + +		v4l2_ctrl_handler_free(hdl); +		kfree(dec); +		return err; +	} + +	/* Initialize tw9906 */ +	dec->norm = V4L2_STD_NTSC; + +	if (write_regs(sd, initial_registers) < 0) { +		v4l2_err(client, "error initializing TW9906\n"); +		kfree(dec); +		return -EINVAL; +	} + +	return 0; +} + +static int tw9906_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 tw9906_id[] = { +	{ "tw9906", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, tw9906_id); + +static struct i2c_driver tw9906_driver = { +	.driver = { +		.owner	= THIS_MODULE, +		.name	= "tw9906", +	}, +	.probe = tw9906_probe, +	.remove = tw9906_remove, +	.id_table = tw9906_id, +}; +module_i2c_driver(tw9906_driver); diff --git a/drivers/media/i2c/uda1342.c b/drivers/media/i2c/uda1342.c new file mode 100644 index 00000000000..3af408556d2 --- /dev/null +++ b/drivers/media/i2c/uda1342.c @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2005-2006 Micronas USA Inc. + * + * 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/uda1342.h> +#include <linux/slab.h> + +static int write_reg(struct i2c_client *client, int reg, int value) +{ +	/* UDA1342 wants MSB first, but SMBus sends LSB first */ +	i2c_smbus_write_word_data(client, reg, swab16(value)); +	return 0; +} + +static int uda1342_s_routing(struct v4l2_subdev *sd, +		u32 input, u32 output, u32 config) +{ +	struct i2c_client *client = v4l2_get_subdevdata(sd); + +	switch (input) { +	case UDA1342_IN1: +		write_reg(client, 0x00, 0x1241); /* select input 1 */ +		break; +	case UDA1342_IN2: +		write_reg(client, 0x00, 0x1441); /* select input 2 */ +		break; +	default: +		v4l2_err(sd, "input %d not supported\n", input); +		break; +	} +	return 0; +} + +static const struct v4l2_subdev_audio_ops uda1342_audio_ops = { +	.s_routing = uda1342_s_routing, +}; + +static const struct v4l2_subdev_ops uda1342_ops = { +	.audio = &uda1342_audio_ops, +}; + +static int uda1342_probe(struct i2c_client *client, +			     const struct i2c_device_id *id) +{ +	struct i2c_adapter *adapter = client->adapter; +	struct v4l2_subdev *sd; + +	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) +		return -ENODEV; + +	dev_dbg(&client->dev, "initializing UDA1342 at address %d on %s\n", +		client->addr, adapter->name); + +	sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); +	if (sd == NULL) +		return -ENOMEM; + +	v4l2_i2c_subdev_init(sd, client, &uda1342_ops); + +	write_reg(client, 0x00, 0x8000); /* reset registers */ +	write_reg(client, 0x00, 0x1241); /* select input 1 */ + +	v4l_info(client, "chip found @ 0x%02x (%s)\n", +			client->addr << 1, client->adapter->name); + +	return 0; +} + +static int uda1342_remove(struct i2c_client *client) +{ +	struct v4l2_subdev *sd = i2c_get_clientdata(client); + +	v4l2_device_unregister_subdev(sd); +	kfree(sd); +	return 0; +} + +static const struct i2c_device_id uda1342_id[] = { +	{ "uda1342", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, uda1342_id); + +static struct i2c_driver uda1342_driver = { +	.driver = { +		.name	= "uda1342", +	}, +	.probe		= uda1342_probe, +	.remove		= uda1342_remove, +	.id_table	= uda1342_id, +}; + +module_i2c_driver(uda1342_driver); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/upd64031a.c b/drivers/media/i2c/upd64031a.c index 1e744654209..f0a09214c51 100644 --- a/drivers/media/i2c/upd64031a.c +++ b/drivers/media/i2c/upd64031a.c @@ -111,7 +111,7 @@ static void upd64031a_write(struct v4l2_subdev *sd, u8 reg, u8 val)  /* ------------------------------------------------------------------------ */  /* The input changed due to new input or channel changed */ -static int upd64031a_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +static int upd64031a_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequency *freq)  {  	struct upd64031a_state *state = to_state(sd);  	u8 reg = state->regs[R00]; @@ -175,7 +175,7 @@ static int upd64031a_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register  	return 0;  } -static int upd64031a_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int upd64031a_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/upd64083.c b/drivers/media/i2c/upd64083.c index 75d6acc6201..343e0215f74 100644 --- a/drivers/media/i2c/upd64083.c +++ b/drivers/media/i2c/upd64083.c @@ -133,7 +133,7 @@ static int upd64083_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register  	return 0;  } -static int upd64083_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int upd64083_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/vp27smpx.c b/drivers/media/i2c/vp27smpx.c index 7cfbc9d94a4..e71f139695a 100644 --- a/drivers/media/i2c/vp27smpx.c +++ b/drivers/media/i2c/vp27smpx.c @@ -90,7 +90,7 @@ static int vp27smpx_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)  	return 0;  } -static int vp27smpx_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +static int vp27smpx_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt)  {  	struct vp27smpx_state *state = to_state(sd); diff --git a/drivers/media/i2c/vs6624.c b/drivers/media/i2c/vs6624.c index 9ac1b8c3a83..f366fad6269 100644 --- a/drivers/media/i2c/vs6624.c +++ b/drivers/media/i2c/vs6624.c @@ -748,7 +748,7 @@ static int vs6624_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *r  	return 0;  } -static int vs6624_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int vs6624_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)  {  	struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/drivers/media/i2c/wm8775.c b/drivers/media/i2c/wm8775.c index bee77ea9f49..27c27b4ae23 100644 --- a/drivers/media/i2c/wm8775.c +++ b/drivers/media/i2c/wm8775.c @@ -174,7 +174,7 @@ static int wm8775_log_status(struct v4l2_subdev *sd)  	return 0;  } -static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +static int wm8775_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequency *freq)  {  	wm8775_set_audio(sd, 0);  	return 0;  |