[media] move i2c files into drivers/media/i2c

Move ancillary I2C drivers into drivers/media/i2c, in order to
better organize them.

Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
Mauro Carvalho Chehab 2012-08-14 16:23:43 -03:00
commit cb7a01ac32
124 changed files with 671 additions and 689 deletions

View file

@ -151,18 +151,15 @@ source "drivers/media/rc/Kconfig"
source "drivers/media/tuners/Kconfig"
#
# Video/Radio/Hybrid adapters
#
source "drivers/media/i2c/Kconfig"
#
# V4L platform/mem2mem drivers
#
source "drivers/media/video/Kconfig"
source "drivers/media/radio/Kconfig"
#
# DVB adapters
#
source "drivers/media/pci/Kconfig"
source "drivers/media/usb/Kconfig"
source "drivers/media/mmc/Kconfig"

View file

@ -9,7 +9,7 @@ ifeq ($(CONFIG_MEDIA_CONTROLLER),y)
endif
obj-y += tuners/ common/ rc/ video/
obj-y += pci/ usb/ mmc/ firewire/ parport/
obj-y += i2c/ pci/ usb/ mmc/ firewire/ parport/
obj-$(CONFIG_VIDEO_DEV) += radio/ v4l2-core/
obj-$(CONFIG_DVB_CORE) += dvb-core/ dvb-frontends/

566
drivers/media/i2c/Kconfig Normal file
View file

@ -0,0 +1,566 @@
#
# Generic video config states
#
config VIDEO_BTCX
depends on PCI
tristate
config VIDEO_TVEEPROM
tristate
depends on I2C
#
# Multimedia Video device configuration
#
if VIDEO_V4L2
config VIDEO_HELPER_CHIPS_AUTO
bool "Autoselect pertinent encoders/decoders and other helper chips"
default y if !EXPERT
---help---
Most video cards may require additional modules to encode or
decode audio/video standards. This option will autoselect
all pertinent modules to each selected video module.
Unselect this only if you know exactly what you are doing, since
it may break support on some boards.
In doubt, say Y.
config VIDEO_IR_I2C
tristate "I2C module for IR" if !VIDEO_HELPER_CHIPS_AUTO
depends on I2C && RC_CORE
default y
---help---
Most boards have an IR chip directly connected via GPIO. However,
some video boards have the IR connected via I2C bus.
If your board doesn't have an I2C IR chip, you may disable this
option.
In doubt, say Y.
#
# Encoder / Decoder module configuration
#
menu "Encoders, decoders, sensors and other helper chips"
visible if !VIDEO_HELPER_CHIPS_AUTO
comment "Audio decoders, processors and mixers"
config VIDEO_TVAUDIO
tristate "Simple audio decoder chips"
depends on VIDEO_V4L2 && I2C
---help---
Support for several audio decoder chips found on some bt8xx boards:
Philips: tda9840, tda9873h, tda9874h/a, tda9850, tda985x, tea6300,
tea6320, tea6420, tda8425, ta8874z.
Microchip: pic16c54 based design on ProVideo PV951 board.
To compile this driver as a module, choose M here: the
module will be called tvaudio.
config VIDEO_TDA7432
tristate "Philips TDA7432 audio processor"
depends on VIDEO_V4L2 && I2C
---help---
Support for tda7432 audio decoder chip found on some bt8xx boards.
To compile this driver as a module, choose M here: the
module will be called tda7432.
config VIDEO_TDA9840
tristate "Philips TDA9840 audio processor"
depends on I2C
---help---
Support for tda9840 audio decoder chip found on some Zoran boards.
To compile this driver as a module, choose M here: the
module will be called tda9840.
config VIDEO_TEA6415C
tristate "Philips TEA6415C audio processor"
depends on I2C
---help---
Support for tea6415c audio decoder chip found on some bt8xx boards.
To compile this driver as a module, choose M here: the
module will be called tea6415c.
config VIDEO_TEA6420
tristate "Philips TEA6420 audio processor"
depends on I2C
---help---
Support for tea6420 audio decoder chip found on some bt8xx boards.
To compile this driver as a module, choose M here: the
module will be called tea6420.
config VIDEO_MSP3400
tristate "Micronas MSP34xx audio decoders"
depends on VIDEO_V4L2 && I2C
---help---
Support for the Micronas MSP34xx series of audio decoders.
To compile this driver as a module, choose M here: the
module will be called msp3400.
config VIDEO_CS5345
tristate "Cirrus Logic CS5345 audio ADC"
depends on VIDEO_V4L2 && I2C
---help---
Support for the Cirrus Logic CS5345 24-bit, 192 kHz
stereo A/D converter.
To compile this driver as a module, choose M here: the
module will be called cs5345.
config VIDEO_CS53L32A
tristate "Cirrus Logic CS53L32A audio ADC"
depends on VIDEO_V4L2 && I2C
---help---
Support for the Cirrus Logic CS53L32A low voltage
stereo A/D converter.
To compile this driver as a module, choose M here: the
module will be called cs53l32a.
config VIDEO_TLV320AIC23B
tristate "Texas Instruments TLV320AIC23B audio codec"
depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
---help---
Support for the Texas Instruments TLV320AIC23B audio codec.
To compile this driver as a module, choose M here: the
module will be called tlv320aic23b.
config VIDEO_WM8775
tristate "Wolfson Microelectronics WM8775 audio ADC with input mixer"
depends on VIDEO_V4L2 && I2C
---help---
Support for the Wolfson Microelectronics WM8775 high
performance stereo A/D Converter with a 4 channel input mixer.
To compile this driver as a module, choose M here: the
module will be called wm8775.
config VIDEO_WM8739
tristate "Wolfson Microelectronics WM8739 stereo audio ADC"
depends on VIDEO_V4L2 && I2C
---help---
Support for the Wolfson Microelectronics WM8739
stereo A/D Converter.
To compile this driver as a module, choose M here: the
module will be called wm8739.
config VIDEO_VP27SMPX
tristate "Panasonic VP27s internal MPX"
depends on VIDEO_V4L2 && I2C
---help---
Support for the internal MPX of the Panasonic VP27s tuner.
To compile this driver as a module, choose M here: the
module will be called vp27smpx.
comment "RDS decoders"
config VIDEO_SAA6588
tristate "SAA6588 Radio Chip RDS decoder support"
depends on VIDEO_V4L2 && I2C
help
Support for this Radio Data System (RDS) decoder. This allows
seeing radio station identification transmitted using this
standard.
To compile this driver as a module, choose M here: the
module will be called saa6588.
comment "Video decoders"
config VIDEO_ADV7180
tristate "Analog Devices ADV7180 decoder"
depends on VIDEO_V4L2 && I2C
---help---
Support for the Analog Devices ADV7180 video decoder.
To compile this driver as a module, choose M here: the
module will be called adv7180.
config VIDEO_ADV7183
tristate "Analog Devices ADV7183 decoder"
depends on VIDEO_V4L2 && I2C
---help---
V4l2 subdevice driver for the Analog Devices
ADV7183 video decoder.
To compile this driver as a module, choose M here: the
module will be called adv7183.
config VIDEO_BT819
tristate "BT819A VideoStream decoder"
depends on VIDEO_V4L2 && I2C
---help---
Support for BT819A video decoder.
To compile this driver as a module, choose M here: the
module will be called bt819.
config VIDEO_BT856
tristate "BT856 VideoStream decoder"
depends on VIDEO_V4L2 && I2C
---help---
Support for BT856 video decoder.
To compile this driver as a module, choose M here: the
module will be called bt856.
config VIDEO_BT866
tristate "BT866 VideoStream decoder"
depends on VIDEO_V4L2 && I2C
---help---
Support for BT866 video decoder.
To compile this driver as a module, choose M here: the
module will be called bt866.
config VIDEO_KS0127
tristate "KS0127 video decoder"
depends on VIDEO_V4L2 && I2C
---help---
Support for KS0127 video decoder.
This chip is used on AverMedia AVS6EYES Zoran-based MJPEG
cards.
To compile this driver as a module, choose M here: the
module will be called ks0127.
config VIDEO_SAA7110
tristate "Philips SAA7110 video decoder"
depends on VIDEO_V4L2 && I2C
---help---
Support for the Philips SAA7110 video decoders.
To compile this driver as a module, choose M here: the
module will be called saa7110.
config VIDEO_SAA711X
tristate "Philips SAA7111/3/4/5 video decoders"
depends on VIDEO_V4L2 && I2C
---help---
Support for the Philips SAA7111/3/4/5 video decoders.
To compile this driver as a module, choose M here: the
module will be called saa7115.
config VIDEO_SAA7191
tristate "Philips SAA7191 video decoder"
depends on VIDEO_V4L2 && I2C
---help---
Support for the Philips SAA7191 video decoder.
To compile this driver as a module, choose M here: the
module will be called saa7191.
config VIDEO_TVP514X
tristate "Texas Instruments TVP514x video decoder"
depends on VIDEO_V4L2 && I2C
---help---
This is a Video4Linux2 sensor-level driver for the TI TVP5146/47
decoder. It is currently working with the TI OMAP3 camera
controller.
To compile this driver as a module, choose M here: the
module will be called tvp514x.
config VIDEO_TVP5150
tristate "Texas Instruments TVP5150 video decoder"
depends on VIDEO_V4L2 && I2C
---help---
Support for the Texas Instruments TVP5150 video decoder.
To compile this driver as a module, choose M here: the
module will be called tvp5150.
config VIDEO_TVP7002
tristate "Texas Instruments TVP7002 video decoder"
depends on VIDEO_V4L2 && I2C
---help---
Support for the Texas Instruments TVP7002 video decoder.
To compile this driver as a module, choose M here: the
module will be called tvp7002.
config VIDEO_VPX3220
tristate "vpx3220a, vpx3216b & vpx3214c video decoders"
depends on VIDEO_V4L2 && I2C
---help---
Support for VPX322x video decoders.
To compile this driver as a module, choose M here: the
module will be called vpx3220.
comment "Video and audio decoders"
config VIDEO_SAA717X
tristate "Philips SAA7171/3/4 audio/video decoders"
depends on VIDEO_V4L2 && I2C
---help---
Support for the Philips SAA7171/3/4 audio/video decoders.
To compile this driver as a module, choose M here: the
module will be called saa717x.
source "drivers/media/i2c/cx25840/Kconfig"
comment "MPEG video encoders"
config VIDEO_CX2341X
tristate "Conexant CX2341x MPEG encoders"
depends on VIDEO_V4L2 && VIDEO_V4L2_COMMON
---help---
Support for the Conexant CX23416 MPEG encoders
and CX23415 MPEG encoder/decoders.
This module currently supports the encoding functions only.
To compile this driver as a module, choose M here: the
module will be called cx2341x.
comment "Video encoders"
config VIDEO_SAA7127
tristate "Philips SAA7127/9 digital video encoders"
depends on VIDEO_V4L2 && I2C
---help---
Support for the Philips SAA7127/9 digital video encoders.
To compile this driver as a module, choose M here: the
module will be called saa7127.
config VIDEO_SAA7185
tristate "Philips SAA7185 video encoder"
depends on VIDEO_V4L2 && I2C
---help---
Support for the Philips SAA7185 video encoder.
To compile this driver as a module, choose M here: the
module will be called saa7185.
config VIDEO_ADV7170
tristate "Analog Devices ADV7170 video encoder"
depends on VIDEO_V4L2 && I2C
---help---
Support for the Analog Devices ADV7170 video encoder driver
To compile this driver as a module, choose M here: the
module will be called adv7170.
config VIDEO_ADV7175
tristate "Analog Devices ADV7175 video encoder"
depends on VIDEO_V4L2 && I2C
---help---
Support for the Analog Devices ADV7175 video encoder driver
To compile this driver as a module, choose M here: the
module will be called adv7175.
config VIDEO_ADV7343
tristate "ADV7343 video encoder"
depends on I2C
help
Support for Analog Devices I2C bus based ADV7343 encoder.
To compile this driver as a module, choose M here: the
module will be called adv7343.
config VIDEO_ADV7393
tristate "ADV7393 video encoder"
depends on I2C
help
Support for Analog Devices I2C bus based ADV7393 encoder.
To compile this driver as a module, choose M here: the
module will be called adv7393.
config VIDEO_AK881X
tristate "AK8813/AK8814 video encoders"
depends on I2C
help
Video output driver for AKM AK8813 and AK8814 TV encoders
comment "Camera sensor devices"
config VIDEO_APTINA_PLL
tristate
config VIDEO_SMIAPP_PLL
tristate
config VIDEO_OV7670
tristate "OmniVision OV7670 sensor support"
depends on I2C && VIDEO_V4L2
depends on MEDIA_CAMERA_SUPPORT
---help---
This is a Video4Linux2 sensor-level driver for the OmniVision
OV7670 VGA camera. It currently only works with the M88ALP01
controller.
config VIDEO_VS6624
tristate "ST VS6624 sensor support"
depends on VIDEO_V4L2 && I2C
depends on MEDIA_CAMERA_SUPPORT
---help---
This is a Video4Linux2 sensor-level driver for the ST VS6624
camera.
To compile this driver as a module, choose M here: the
module will be called vs6624.
config VIDEO_MT9M032
tristate "MT9M032 camera sensor support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
depends on MEDIA_CAMERA_SUPPORT
select VIDEO_APTINA_PLL
---help---
This driver supports MT9M032 camera sensors from Aptina, monochrome
models only.
config VIDEO_MT9P031
tristate "Aptina MT9P031 support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
depends on MEDIA_CAMERA_SUPPORT
select VIDEO_APTINA_PLL
---help---
This is a Video4Linux2 sensor-level driver for the Aptina
(Micron) mt9p031 5 Mpixel camera.
config VIDEO_MT9T001
tristate "Aptina MT9T001 support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
depends on MEDIA_CAMERA_SUPPORT
---help---
This is a Video4Linux2 sensor-level driver for the Aptina
(Micron) mt0t001 3 Mpixel camera.
config VIDEO_MT9V011
tristate "Micron mt9v011 sensor support"
depends on I2C && VIDEO_V4L2
depends on MEDIA_CAMERA_SUPPORT
---help---
This is a Video4Linux2 sensor-level driver for the Micron
mt0v011 1.3 Mpixel camera. It currently only works with the
em28xx driver.
config VIDEO_MT9V032
tristate "Micron MT9V032 sensor support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
depends on MEDIA_CAMERA_SUPPORT
---help---
This is a Video4Linux2 sensor-level driver for the Micron
MT9V032 752x480 CMOS sensor.
config VIDEO_TCM825X
tristate "TCM825x camera sensor support"
depends on I2C && VIDEO_V4L2
depends on MEDIA_CAMERA_SUPPORT
---help---
This is a driver for the Toshiba TCM825x VGA camera sensor.
It is used for example in Nokia N800.
config VIDEO_SR030PC30
tristate "Siliconfile SR030PC30 sensor support"
depends on I2C && VIDEO_V4L2
depends on MEDIA_CAMERA_SUPPORT
---help---
This driver supports SR030PC30 VGA camera from Siliconfile
config VIDEO_NOON010PC30
tristate "Siliconfile NOON010PC30 sensor support"
depends on I2C && VIDEO_V4L2 && EXPERIMENTAL && VIDEO_V4L2_SUBDEV_API
depends on MEDIA_CAMERA_SUPPORT
---help---
This driver supports NOON010PC30 CIF camera from Siliconfile
source "drivers/media/i2c/m5mols/Kconfig"
config VIDEO_S5K6AA
tristate "Samsung S5K6AAFX sensor support"
depends on MEDIA_CAMERA_SUPPORT
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
---help---
This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M
camera sensor with an embedded SoC image signal processor.
source "drivers/media/i2c/smiapp/Kconfig"
comment "Flash devices"
config VIDEO_ADP1653
tristate "ADP1653 flash support"
depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
depends on MEDIA_CAMERA_SUPPORT
---help---
This is a driver for the ADP1653 flash controller. It is used for
example in Nokia N900.
config VIDEO_AS3645A
tristate "AS3645A flash driver support"
depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
depends on MEDIA_CAMERA_SUPPORT
---help---
This is a driver for the AS3645A and LM3555 flash controllers. It has
build in control for flash, torch and indicator LEDs.
comment "Video improvement chips"
config VIDEO_UPD64031A
tristate "NEC Electronics uPD64031A Ghost Reduction"
depends on VIDEO_V4L2 && I2C
---help---
Support for the NEC Electronics uPD64031A Ghost Reduction
video chip. It is most often found in NTSC TV cards made for
Japan and is used to reduce the 'ghosting' effect that can
be present in analog TV broadcasts.
To compile this driver as a module, choose M here: the
module will be called upd64031a.
config VIDEO_UPD64083
tristate "NEC Electronics uPD64083 3-Dimensional Y/C separation"
depends on VIDEO_V4L2 && I2C
---help---
Support for the NEC Electronics uPD64083 3-Dimensional Y/C
separation video chip. It is used to improve the quality of
the colors of a composite signal.
To compile this driver as a module, choose M here: the
module will be called upd64083.
comment "Miscelaneous helper chips"
config VIDEO_THS7303
tristate "THS7303 Video Amplifier"
depends on I2C
help
Support for TI THS7303 video amplifier
To compile this driver as a module, choose M here: the
module will be called ths7303.
config VIDEO_M52790
tristate "Mitsubishi M52790 A/V switch"
depends on VIDEO_V4L2 && I2C
---help---
Support for the Mitsubishi M52790 A/V switch.
To compile this driver as a module, choose M here: the
module will be called m52790.
endmenu
endif

View file

@ -0,0 +1,63 @@
msp3400-objs := msp3400-driver.o msp3400-kthreads.o
obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o
obj-$(CONFIG_VIDEO_SMIAPP) += smiapp/
obj-$(CONFIG_VIDEO_CX25840) += cx25840/
obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/
obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o
obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o
obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o
obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o
obj-$(CONFIG_VIDEO_TDA9840) += tda9840.o
obj-$(CONFIG_VIDEO_TEA6415C) += tea6415c.o
obj-$(CONFIG_VIDEO_TEA6420) += tea6420.o
obj-$(CONFIG_VIDEO_SAA7110) += saa7110.o
obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o
obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o
obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o
obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o
obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o
obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o
obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o
obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o
obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o
obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o
obj-$(CONFIG_VIDEO_ADV7393) += adv7393.o
obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o
obj-$(CONFIG_VIDEO_VS6624) += vs6624.o
obj-$(CONFIG_VIDEO_BT819) += bt819.o
obj-$(CONFIG_VIDEO_BT856) += bt856.o
obj-$(CONFIG_VIDEO_BT866) += bt866.o
obj-$(CONFIG_VIDEO_KS0127) += ks0127.o
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_CS5345) += cs5345.o
obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o
obj-$(CONFIG_VIDEO_M52790) += m52790.o
obj-$(CONFIG_VIDEO_TLV320AIC23B) += tlv320aic23b.o
obj-$(CONFIG_VIDEO_WM8775) += wm8775.o
obj-$(CONFIG_VIDEO_WM8739) += wm8739.o
obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o
obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o
obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o
obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o
obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o
obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o
obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o
obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o
obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o
obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o
obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o
obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o
obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o
obj-$(CONFIG_VIDEO_AK881X) += ak881x.o
obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o

487
drivers/media/i2c/adp1653.c Normal file
View file

@ -0,0 +1,487 @@
/*
* drivers/media/i2c/adp1653.c
*
* Copyright (C) 2008--2011 Nokia Corporation
*
* Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
*
* Contributors:
* Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
* Tuukka Toivonen <tuukkat76@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* TODO:
* - fault interrupt handling
* - hardware strobe
* - power doesn't need to be ON if all lights are off
*
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <media/adp1653.h>
#include <media/v4l2-device.h>
#define TIMEOUT_MAX 820000
#define TIMEOUT_STEP 54600
#define TIMEOUT_MIN (TIMEOUT_MAX - ADP1653_REG_CONFIG_TMR_SET_MAX \
* TIMEOUT_STEP)
#define TIMEOUT_US_TO_CODE(t) ((TIMEOUT_MAX + (TIMEOUT_STEP / 2) - (t)) \
/ TIMEOUT_STEP)
#define TIMEOUT_CODE_TO_US(c) (TIMEOUT_MAX - (c) * TIMEOUT_STEP)
/* Write values into ADP1653 registers. */
static int adp1653_update_hw(struct adp1653_flash *flash)
{
struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
u8 out_sel;
u8 config = 0;
int rval;
out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG(
flash->indicator_intensity->val)
<< ADP1653_REG_OUT_SEL_ILED_SHIFT;
switch (flash->led_mode->val) {
case V4L2_FLASH_LED_MODE_NONE:
break;
case V4L2_FLASH_LED_MODE_FLASH:
/* Flash mode, light on with strobe, duration from timer */
config = ADP1653_REG_CONFIG_TMR_CFG;
config |= TIMEOUT_US_TO_CODE(flash->flash_timeout->val)
<< ADP1653_REG_CONFIG_TMR_SET_SHIFT;
break;
case V4L2_FLASH_LED_MODE_TORCH:
/* Torch mode, light immediately on, duration indefinite */
out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG(
flash->torch_intensity->val)
<< ADP1653_REG_OUT_SEL_HPLED_SHIFT;
break;
}
rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel);
if (rval < 0)
return rval;
rval = i2c_smbus_write_byte_data(client, ADP1653_REG_CONFIG, config);
if (rval < 0)
return rval;
return 0;
}
static int adp1653_get_fault(struct adp1653_flash *flash)
{
struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
int fault;
int rval;
fault = i2c_smbus_read_byte_data(client, ADP1653_REG_FAULT);
if (IS_ERR_VALUE(fault))
return fault;
flash->fault |= fault;
if (!flash->fault)
return 0;
/* Clear faults. */
rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0);
if (IS_ERR_VALUE(rval))
return rval;
flash->led_mode->val = V4L2_FLASH_LED_MODE_NONE;
rval = adp1653_update_hw(flash);
if (IS_ERR_VALUE(rval))
return rval;
return flash->fault;
}
static int adp1653_strobe(struct adp1653_flash *flash, int enable)
{
struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
u8 out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG(
flash->indicator_intensity->val)
<< ADP1653_REG_OUT_SEL_ILED_SHIFT;
int rval;
if (flash->led_mode->val != V4L2_FLASH_LED_MODE_FLASH)
return -EBUSY;
if (!enable)
return i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL,
out_sel);
out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG(
flash->flash_intensity->val)
<< ADP1653_REG_OUT_SEL_HPLED_SHIFT;
rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel);
if (rval)
return rval;
/* Software strobe using i2c */
rval = i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE,
ADP1653_REG_SW_STROBE_SW_STROBE);
if (rval)
return rval;
return i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE, 0);
}
/* --------------------------------------------------------------------------
* V4L2 controls
*/
static int adp1653_get_ctrl(struct v4l2_ctrl *ctrl)
{
struct adp1653_flash *flash =
container_of(ctrl->handler, struct adp1653_flash, ctrls);
int rval;
rval = adp1653_get_fault(flash);
if (IS_ERR_VALUE(rval))
return rval;
ctrl->cur.val = 0;
if (flash->fault & ADP1653_REG_FAULT_FLT_SCP)
ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
if (flash->fault & ADP1653_REG_FAULT_FLT_OT)
ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE;
if (flash->fault & ADP1653_REG_FAULT_FLT_TMR)
ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT;
if (flash->fault & ADP1653_REG_FAULT_FLT_OV)
ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE;
flash->fault = 0;
return 0;
}
static int adp1653_set_ctrl(struct v4l2_ctrl *ctrl)
{
struct adp1653_flash *flash =
container_of(ctrl->handler, struct adp1653_flash, ctrls);
int rval;
rval = adp1653_get_fault(flash);
if (IS_ERR_VALUE(rval))
return rval;
if ((rval & (ADP1653_REG_FAULT_FLT_SCP |
ADP1653_REG_FAULT_FLT_OT |
ADP1653_REG_FAULT_FLT_OV)) &&
(ctrl->id == V4L2_CID_FLASH_STROBE ||
ctrl->id == V4L2_CID_FLASH_TORCH_INTENSITY ||
ctrl->id == V4L2_CID_FLASH_LED_MODE))
return -EBUSY;
switch (ctrl->id) {
case V4L2_CID_FLASH_STROBE:
return adp1653_strobe(flash, 1);
case V4L2_CID_FLASH_STROBE_STOP:
return adp1653_strobe(flash, 0);
}
return adp1653_update_hw(flash);
}
static const struct v4l2_ctrl_ops adp1653_ctrl_ops = {
.g_volatile_ctrl = adp1653_get_ctrl,
.s_ctrl = adp1653_set_ctrl,
};
static int adp1653_init_controls(struct adp1653_flash *flash)
{
struct v4l2_ctrl *fault;
v4l2_ctrl_handler_init(&flash->ctrls, 9);
flash->led_mode =
v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops,
V4L2_CID_FLASH_LED_MODE,
V4L2_FLASH_LED_MODE_TORCH, ~0x7, 0);
v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops,
V4L2_CID_FLASH_STROBE_SOURCE,
V4L2_FLASH_STROBE_SOURCE_SOFTWARE, ~0x1, 0);
v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
V4L2_CID_FLASH_STROBE, 0, 0, 0, 0);
v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0);
flash->flash_timeout =
v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
V4L2_CID_FLASH_TIMEOUT, TIMEOUT_MIN,
flash->platform_data->max_flash_timeout,
TIMEOUT_STEP,
flash->platform_data->max_flash_timeout);
flash->flash_intensity =
v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
V4L2_CID_FLASH_INTENSITY,
ADP1653_FLASH_INTENSITY_MIN,
flash->platform_data->max_flash_intensity,
1, flash->platform_data->max_flash_intensity);
flash->torch_intensity =
v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
V4L2_CID_FLASH_TORCH_INTENSITY,
ADP1653_TORCH_INTENSITY_MIN,
flash->platform_data->max_torch_intensity,
ADP1653_FLASH_INTENSITY_STEP,
flash->platform_data->max_torch_intensity);
flash->indicator_intensity =
v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
V4L2_CID_FLASH_INDICATOR_INTENSITY,
ADP1653_INDICATOR_INTENSITY_MIN,
flash->platform_data->max_indicator_intensity,
ADP1653_INDICATOR_INTENSITY_STEP,
ADP1653_INDICATOR_INTENSITY_MIN);
fault = v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
V4L2_CID_FLASH_FAULT, 0,
V4L2_FLASH_FAULT_OVER_VOLTAGE
| V4L2_FLASH_FAULT_OVER_TEMPERATURE
| V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0);
if (flash->ctrls.error)
return flash->ctrls.error;
fault->flags |= V4L2_CTRL_FLAG_VOLATILE;
flash->subdev.ctrl_handler = &flash->ctrls;
return 0;
}
/* --------------------------------------------------------------------------
* V4L2 subdev operations
*/
static int
adp1653_init_device(struct adp1653_flash *flash)
{
struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
int rval;
/* Clear FAULT register by writing zero to OUT_SEL */
rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0);
if (rval < 0) {
dev_err(&client->dev, "failed writing fault register\n");
return -EIO;
}
mutex_lock(flash->ctrls.lock);
/* Reset faults before reading new ones. */
flash->fault = 0;
rval = adp1653_get_fault(flash);
mutex_unlock(flash->ctrls.lock);
if (rval > 0) {
dev_err(&client->dev, "faults detected: 0x%1.1x\n", rval);
return -EIO;
}
mutex_lock(flash->ctrls.lock);
rval = adp1653_update_hw(flash);
mutex_unlock(flash->ctrls.lock);
if (rval) {
dev_err(&client->dev,
"adp1653_update_hw failed at %s\n", __func__);
return -EIO;
}
return 0;
}
static int
__adp1653_set_power(struct adp1653_flash *flash, int on)
{
int ret;
ret = flash->platform_data->power(&flash->subdev, on);
if (ret < 0)
return ret;
if (!on)
return 0;
ret = adp1653_init_device(flash);
if (ret < 0)
flash->platform_data->power(&flash->subdev, 0);
return ret;
}
static int
adp1653_set_power(struct v4l2_subdev *subdev, int on)
{
struct adp1653_flash *flash = to_adp1653_flash(subdev);
int ret = 0;
mutex_lock(&flash->power_lock);
/* If the power count is modified from 0 to != 0 or from != 0 to 0,
* update the power state.
*/
if (flash->power_count == !on) {
ret = __adp1653_set_power(flash, !!on);
if (ret < 0)
goto done;
}
/* Update the power count. */
flash->power_count += on ? 1 : -1;
WARN_ON(flash->power_count < 0);
done:
mutex_unlock(&flash->power_lock);
return ret;
}
static int adp1653_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
return adp1653_set_power(sd, 1);
}
static int adp1653_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
return adp1653_set_power(sd, 0);
}
static const struct v4l2_subdev_core_ops adp1653_core_ops = {
.s_power = adp1653_set_power,
};
static const struct v4l2_subdev_ops adp1653_ops = {
.core = &adp1653_core_ops,
};
static const struct v4l2_subdev_internal_ops adp1653_internal_ops = {
.open = adp1653_open,
.close = adp1653_close,
};
/* --------------------------------------------------------------------------
* I2C driver
*/
#ifdef CONFIG_PM
static int adp1653_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct v4l2_subdev *subdev = i2c_get_clientdata(client);
struct adp1653_flash *flash = to_adp1653_flash(subdev);
if (!flash->power_count)
return 0;
return __adp1653_set_power(flash, 0);
}
static int adp1653_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct v4l2_subdev *subdev = i2c_get_clientdata(client);
struct adp1653_flash *flash = to_adp1653_flash(subdev);
if (!flash->power_count)
return 0;
return __adp1653_set_power(flash, 1);
}
#else
#define adp1653_suspend NULL
#define adp1653_resume NULL
#endif /* CONFIG_PM */
static int adp1653_probe(struct i2c_client *client,
const struct i2c_device_id *devid)
{
struct adp1653_flash *flash;
int ret;
/* we couldn't work without platform data */
if (client->dev.platform_data == NULL)
return -ENODEV;
flash = kzalloc(sizeof(*flash), GFP_KERNEL);
if (flash == NULL)
return -ENOMEM;
flash->platform_data = client->dev.platform_data;
mutex_init(&flash->power_lock);
v4l2_i2c_subdev_init(&flash->subdev, client, &adp1653_ops);
flash->subdev.internal_ops = &adp1653_internal_ops;
flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
ret = adp1653_init_controls(flash);
if (ret)
goto free_and_quit;
ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0);
if (ret < 0)
goto free_and_quit;
flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
return 0;
free_and_quit:
v4l2_ctrl_handler_free(&flash->ctrls);
kfree(flash);
return ret;
}
static int __exit adp1653_remove(struct i2c_client *client)
{
struct v4l2_subdev *subdev = i2c_get_clientdata(client);
struct adp1653_flash *flash = to_adp1653_flash(subdev);
v4l2_device_unregister_subdev(&flash->subdev);
v4l2_ctrl_handler_free(&flash->ctrls);
media_entity_cleanup(&flash->subdev.entity);
kfree(flash);
return 0;
}
static const struct i2c_device_id adp1653_id_table[] = {
{ ADP1653_NAME, 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adp1653_id_table);
static struct dev_pm_ops adp1653_pm_ops = {
.suspend = adp1653_suspend,
.resume = adp1653_resume,
};
static struct i2c_driver adp1653_i2c_driver = {
.driver = {
.name = ADP1653_NAME,
.pm = &adp1653_pm_ops,
},
.probe = adp1653_probe,
.remove = __exit_p(adp1653_remove),
.id_table = adp1653_id_table,
};
module_i2c_driver(adp1653_i2c_driver);
MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
MODULE_DESCRIPTION("Analog Devices ADP1653 LED flash driver");
MODULE_LICENSE("GPL");

410
drivers/media/i2c/adv7170.c Normal file
View file

@ -0,0 +1,410 @@
/*
* adv7170 - adv7170, adv7171 video encoder driver version 0.0.1
*
* Copyright (C) 2002 Maxim Yevtyushkin <max@linuxmedialabs.com>
*
* Based on adv7176 driver by:
*
* Copyright (C) 1998 Dave Perks <dperks@ibm.net>
* Copyright (C) 1999 Wolfgang Scherr <scherr@net4you.net>
* Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
* - some corrections for Pinnacle Systems Inc. DC10plus card.
*
* Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
* - moved over to linux>=2.4.x i2c protocol (1/1/2003)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <asm/uaccess.h>
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
MODULE_DESCRIPTION("Analog Devices ADV7170 video encoder driver");
MODULE_AUTHOR("Maxim Yevtyushkin");
MODULE_LICENSE("GPL");
static int debug;
module_param(debug, int, 0);
MODULE_PARM_DESC(debug, "Debug level (0-1)");
/* ----------------------------------------------------------------------- */
struct adv7170 {
struct v4l2_subdev sd;
unsigned char reg[128];
v4l2_std_id norm;
int input;
};
static inline struct adv7170 *to_adv7170(struct v4l2_subdev *sd)
{
return container_of(sd, struct adv7170, sd);
}
static char *inputs[] = { "pass_through", "play_back" };
static enum v4l2_mbus_pixelcode adv7170_codes[] = {
V4L2_MBUS_FMT_UYVY8_2X8,
V4L2_MBUS_FMT_UYVY8_1X16,
};
/* ----------------------------------------------------------------------- */
static inline int adv7170_write(struct v4l2_subdev *sd, u8 reg, u8 value)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct adv7170 *encoder = to_adv7170(sd);
encoder->reg[reg] = value;
return i2c_smbus_write_byte_data(client, reg, value);
}
static inline int adv7170_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 adv7170_write_block(struct v4l2_subdev *sd,
const u8 *data, unsigned int len)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct adv7170 *encoder = to_adv7170(sd);
int ret = -1;
u8 reg;
/* the adv7170 has an autoincrement function, use it if
* the adapter understands raw I2C */
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
/* do raw I2C, not smbus compatible */
u8 block_data[32];
int block_len;
while (len >= 2) {
block_len = 0;
block_data[block_len++] = reg = data[0];
do {
block_data[block_len++] =
encoder->reg[reg++] = data[1];
len -= 2;
data += 2;
} while (len >= 2 && data[0] == reg && block_len < 32);
ret = i2c_master_send(client, block_data, block_len);
if (ret < 0)
break;
}
} else {
/* do some slow I2C emulation kind of thing */
while (len >= 2) {
reg = *data++;
ret = adv7170_write(sd, reg, *data++);
if (ret < 0)
break;
len -= 2;
}
}
return ret;
}
/* ----------------------------------------------------------------------- */
#define TR0MODE 0x4c
#define TR0RST 0x80
#define TR1CAPT 0x00
#define TR1PLAY 0x00
static const unsigned char init_NTSC[] = {
0x00, 0x10, /* MR0 */
0x01, 0x20, /* MR1 */
0x02, 0x0e, /* MR2 RTC control: bits 2 and 1 */
0x03, 0x80, /* MR3 */
0x04, 0x30, /* MR4 */
0x05, 0x00, /* Reserved */
0x06, 0x00, /* Reserved */
0x07, TR0MODE, /* TM0 */
0x08, TR1CAPT, /* TM1 */
0x09, 0x16, /* Fsc0 */
0x0a, 0x7c, /* Fsc1 */
0x0b, 0xf0, /* Fsc2 */
0x0c, 0x21, /* Fsc3 */
0x0d, 0x00, /* Subcarrier Phase */
0x0e, 0x00, /* Closed Capt. Ext 0 */
0x0f, 0x00, /* Closed Capt. Ext 1 */
0x10, 0x00, /* Closed Capt. 0 */
0x11, 0x00, /* Closed Capt. 1 */
0x12, 0x00, /* Pedestal Ctl 0 */
0x13, 0x00, /* Pedestal Ctl 1 */
0x14, 0x00, /* Pedestal Ctl 2 */
0x15, 0x00, /* Pedestal Ctl 3 */
0x16, 0x00, /* CGMS_WSS_0 */
0x17, 0x00, /* CGMS_WSS_1 */
0x18, 0x00, /* CGMS_WSS_2 */
0x19, 0x00, /* Teletext Ctl */
};
static const unsigned char init_PAL[] = {
0x00, 0x71, /* MR0 */
0x01, 0x20, /* MR1 */
0x02, 0x0e, /* MR2 RTC control: bits 2 and 1 */
0x03, 0x80, /* MR3 */
0x04, 0x30, /* MR4 */
0x05, 0x00, /* Reserved */
0x06, 0x00, /* Reserved */
0x07, TR0MODE, /* TM0 */
0x08, TR1CAPT, /* TM1 */
0x09, 0xcb, /* Fsc0 */
0x0a, 0x8a, /* Fsc1 */
0x0b, 0x09, /* Fsc2 */
0x0c, 0x2a, /* Fsc3 */
0x0d, 0x00, /* Subcarrier Phase */
0x0e, 0x00, /* Closed Capt. Ext 0 */
0x0f, 0x00, /* Closed Capt. Ext 1 */
0x10, 0x00, /* Closed Capt. 0 */
0x11, 0x00, /* Closed Capt. 1 */
0x12, 0x00, /* Pedestal Ctl 0 */
0x13, 0x00, /* Pedestal Ctl 1 */
0x14, 0x00, /* Pedestal Ctl 2 */
0x15, 0x00, /* Pedestal Ctl 3 */
0x16, 0x00, /* CGMS_WSS_0 */
0x17, 0x00, /* CGMS_WSS_1 */
0x18, 0x00, /* CGMS_WSS_2 */
0x19, 0x00, /* Teletext Ctl */
};
static int adv7170_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
{
struct adv7170 *encoder = to_adv7170(sd);
v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std);
if (std & V4L2_STD_NTSC) {
adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC));
if (encoder->input == 0)
adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */
adv7170_write(sd, 0x07, TR0MODE | TR0RST);
adv7170_write(sd, 0x07, TR0MODE);
} else if (std & V4L2_STD_PAL) {
adv7170_write_block(sd, init_PAL, sizeof(init_PAL));
if (encoder->input == 0)
adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */
adv7170_write(sd, 0x07, TR0MODE | TR0RST);
adv7170_write(sd, 0x07, TR0MODE);
} else {
v4l2_dbg(1, debug, sd, "illegal norm: %llx\n",
(unsigned long long)std);
return -EINVAL;
}
v4l2_dbg(1, debug, sd, "switched to %llx\n", (unsigned long long)std);
encoder->norm = std;
return 0;
}
static int adv7170_s_routing(struct v4l2_subdev *sd,
u32 input, u32 output, u32 config)
{
struct adv7170 *encoder = to_adv7170(sd);
/* RJ: input = 0: input is from decoder
input = 1: input is from ZR36060
input = 2: color bar */
v4l2_dbg(1, debug, sd, "set input from %s\n",
input == 0 ? "decoder" : "ZR36060");
switch (input) {
case 0:
adv7170_write(sd, 0x01, 0x20);
adv7170_write(sd, 0x08, TR1CAPT); /* TR1 */
adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */
adv7170_write(sd, 0x07, TR0MODE | TR0RST);
adv7170_write(sd, 0x07, TR0MODE);
/* udelay(10); */
break;
case 1:
adv7170_write(sd, 0x01, 0x00);
adv7170_write(sd, 0x08, TR1PLAY); /* TR1 */
adv7170_write(sd, 0x02, 0x08);
adv7170_write(sd, 0x07, TR0MODE | TR0RST);
adv7170_write(sd, 0x07, TR0MODE);
/* udelay(10); */
break;
default:
v4l2_dbg(1, debug, sd, "illegal input: %d\n", input);
return -EINVAL;
}
v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[input]);
encoder->input = input;
return 0;
}
static int adv7170_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
enum v4l2_mbus_pixelcode *code)
{
if (index >= ARRAY_SIZE(adv7170_codes))
return -EINVAL;
*code = adv7170_codes[index];
return 0;
}
static int adv7170_g_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
u8 val = adv7170_read(sd, 0x7);
if ((val & 0x40) == (1 << 6))
mf->code = V4L2_MBUS_FMT_UYVY8_1X16;
else
mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
mf->width = 0;
mf->height = 0;
mf->field = V4L2_FIELD_ANY;
return 0;
}
static int adv7170_s_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
u8 val = adv7170_read(sd, 0x7);
int ret;
switch (mf->code) {
case V4L2_MBUS_FMT_UYVY8_2X8:
val &= ~0x40;
break;
case V4L2_MBUS_FMT_UYVY8_1X16:
val |= 0x40;
break;
default:
v4l2_dbg(1, debug, sd,
"illegal v4l2_mbus_framefmt code: %d\n", mf->code);
return -EINVAL;
}
ret = adv7170_write(sd, 0x7, val);
return ret;
}
static int adv7170_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7170, 0);
}
/* ----------------------------------------------------------------------- */
static const struct v4l2_subdev_core_ops adv7170_core_ops = {
.g_chip_ident = adv7170_g_chip_ident,
};
static const struct v4l2_subdev_video_ops adv7170_video_ops = {
.s_std_output = adv7170_s_std_output,
.s_routing = adv7170_s_routing,
.s_mbus_fmt = adv7170_s_fmt,
.g_mbus_fmt = adv7170_g_fmt,
.enum_mbus_fmt = adv7170_enum_fmt,
};
static const struct v4l2_subdev_ops adv7170_ops = {
.core = &adv7170_core_ops,
.video = &adv7170_video_ops,
};
/* ----------------------------------------------------------------------- */
static int adv7170_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct adv7170 *encoder;
struct v4l2_subdev *sd;
int i;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
encoder = kzalloc(sizeof(struct adv7170), GFP_KERNEL);
if (encoder == NULL)
return -ENOMEM;
sd = &encoder->sd;
v4l2_i2c_subdev_init(sd, client, &adv7170_ops);
encoder->norm = V4L2_STD_NTSC;
encoder->input = 0;
i = adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC));
if (i >= 0) {
i = adv7170_write(sd, 0x07, TR0MODE | TR0RST);
i = adv7170_write(sd, 0x07, TR0MODE);
i = adv7170_read(sd, 0x12);
v4l2_dbg(1, debug, sd, "revision %d\n", i & 1);
}
if (i < 0)
v4l2_dbg(1, debug, sd, "init error 0x%x\n", i);
return 0;
}
static int adv7170_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(sd);
kfree(to_adv7170(sd));
return 0;
}
/* ----------------------------------------------------------------------- */
static const struct i2c_device_id adv7170_id[] = {
{ "adv7170", 0 },
{ "adv7171", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adv7170_id);
static struct i2c_driver adv7170_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "adv7170",
},
.probe = adv7170_probe,
.remove = adv7170_remove,
.id_table = adv7170_id,
};
module_i2c_driver(adv7170_driver);

460
drivers/media/i2c/adv7175.c Normal file
View file

@ -0,0 +1,460 @@
/*
* adv7175 - adv7175a video encoder driver version 0.0.3
*
* Copyright (C) 1998 Dave Perks <dperks@ibm.net>
* Copyright (C) 1999 Wolfgang Scherr <scherr@net4you.net>
* Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
* - some corrections for Pinnacle Systems Inc. DC10plus card.
*
* Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
* - moved over to linux>=2.4.x i2c protocol (9/9/2002)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <asm/uaccess.h>
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
MODULE_DESCRIPTION("Analog Devices ADV7175 video encoder driver");
MODULE_AUTHOR("Dave Perks");
MODULE_LICENSE("GPL");
#define I2C_ADV7175 0xd4
#define I2C_ADV7176 0x54
static int debug;
module_param(debug, int, 0);
MODULE_PARM_DESC(debug, "Debug level (0-1)");
/* ----------------------------------------------------------------------- */
struct adv7175 {
struct v4l2_subdev sd;
v4l2_std_id norm;
int input;
};
static inline struct adv7175 *to_adv7175(struct v4l2_subdev *sd)
{
return container_of(sd, struct adv7175, sd);
}
static char *inputs[] = { "pass_through", "play_back", "color_bar" };
static enum v4l2_mbus_pixelcode adv7175_codes[] = {
V4L2_MBUS_FMT_UYVY8_2X8,
V4L2_MBUS_FMT_UYVY8_1X16,
};
/* ----------------------------------------------------------------------- */
static inline int adv7175_write(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 inline int adv7175_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 adv7175_write_block(struct v4l2_subdev *sd,
const u8 *data, unsigned int len)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret = -1;
u8 reg;
/* the adv7175 has an autoincrement function, use it if
* the adapter understands raw I2C */
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
/* do raw I2C, not smbus compatible */
u8 block_data[32];
int block_len;
while (len >= 2) {
block_len = 0;
block_data[block_len++] = reg = data[0];
do {
block_data[block_len++] = data[1];
reg++;
len -= 2;
data += 2;
} while (len >= 2 && data[0] == reg && block_len < 32);
ret = i2c_master_send(client, block_data, block_len);
if (ret < 0)
break;
}
} else {
/* do some slow I2C emulation kind of thing */
while (len >= 2) {
reg = *data++;
ret = adv7175_write(sd, reg, *data++);
if (ret < 0)
break;
len -= 2;
}
}
return ret;
}
static void set_subcarrier_freq(struct v4l2_subdev *sd, int pass_through)
{
/* for some reason pass_through NTSC needs
* a different sub-carrier freq to remain stable. */
if (pass_through)
adv7175_write(sd, 0x02, 0x00);
else
adv7175_write(sd, 0x02, 0x55);
adv7175_write(sd, 0x03, 0x55);
adv7175_write(sd, 0x04, 0x55);
adv7175_write(sd, 0x05, 0x25);
}
/* ----------------------------------------------------------------------- */
/* Output filter: S-Video Composite */
#define MR050 0x11 /* 0x09 */
#define MR060 0x14 /* 0x0c */
/* ----------------------------------------------------------------------- */
#define TR0MODE 0x46
#define TR0RST 0x80
#define TR1CAPT 0x80
#define TR1PLAY 0x00
static const unsigned char init_common[] = {
0x00, MR050, /* MR0, PAL enabled */
0x01, 0x00, /* MR1 */
0x02, 0x0c, /* subc. freq. */
0x03, 0x8c, /* subc. freq. */
0x04, 0x79, /* subc. freq. */
0x05, 0x26, /* subc. freq. */
0x06, 0x40, /* subc. phase */
0x07, TR0MODE, /* TR0, 16bit */
0x08, 0x21, /* */
0x09, 0x00, /* */
0x0a, 0x00, /* */
0x0b, 0x00, /* */
0x0c, TR1CAPT, /* TR1 */
0x0d, 0x4f, /* MR2 */
0x0e, 0x00, /* */
0x0f, 0x00, /* */
0x10, 0x00, /* */
0x11, 0x00, /* */
};
static const unsigned char init_pal[] = {
0x00, MR050, /* MR0, PAL enabled */
0x01, 0x00, /* MR1 */
0x02, 0x0c, /* subc. freq. */
0x03, 0x8c, /* subc. freq. */
0x04, 0x79, /* subc. freq. */
0x05, 0x26, /* subc. freq. */
0x06, 0x40, /* subc. phase */
};
static const unsigned char init_ntsc[] = {
0x00, MR060, /* MR0, NTSC enabled */
0x01, 0x00, /* MR1 */
0x02, 0x55, /* subc. freq. */
0x03, 0x55, /* subc. freq. */
0x04, 0x55, /* subc. freq. */
0x05, 0x25, /* subc. freq. */
0x06, 0x1a, /* subc. phase */
};
static int adv7175_init(struct v4l2_subdev *sd, u32 val)
{
/* This is just for testing!!! */
adv7175_write_block(sd, init_common, sizeof(init_common));
adv7175_write(sd, 0x07, TR0MODE | TR0RST);
adv7175_write(sd, 0x07, TR0MODE);
return 0;
}
static int adv7175_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
{
struct adv7175 *encoder = to_adv7175(sd);
if (std & V4L2_STD_NTSC) {
adv7175_write_block(sd, init_ntsc, sizeof(init_ntsc));
if (encoder->input == 0)
adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */
adv7175_write(sd, 0x07, TR0MODE | TR0RST);
adv7175_write(sd, 0x07, TR0MODE);
} else if (std & V4L2_STD_PAL) {
adv7175_write_block(sd, init_pal, sizeof(init_pal));
if (encoder->input == 0)
adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */
adv7175_write(sd, 0x07, TR0MODE | TR0RST);
adv7175_write(sd, 0x07, TR0MODE);
} else if (std & V4L2_STD_SECAM) {
/* This is an attempt to convert
* SECAM->PAL (typically it does not work
* due to genlock: when decoder is in SECAM
* and encoder in in PAL the subcarrier can
* not be syncronized with horizontal
* quency) */
adv7175_write_block(sd, init_pal, sizeof(init_pal));
if (encoder->input == 0)
adv7175_write(sd, 0x0d, 0x49); /* Disable genlock */
adv7175_write(sd, 0x07, TR0MODE | TR0RST);
adv7175_write(sd, 0x07, TR0MODE);
} else {
v4l2_dbg(1, debug, sd, "illegal norm: %llx\n",
(unsigned long long)std);
return -EINVAL;
}
v4l2_dbg(1, debug, sd, "switched to %llx\n", (unsigned long long)std);
encoder->norm = std;
return 0;
}
static int adv7175_s_routing(struct v4l2_subdev *sd,
u32 input, u32 output, u32 config)
{
struct adv7175 *encoder = to_adv7175(sd);
/* RJ: input = 0: input is from decoder
input = 1: input is from ZR36060
input = 2: color bar */
switch (input) {
case 0:
adv7175_write(sd, 0x01, 0x00);
if (encoder->norm & V4L2_STD_NTSC)
set_subcarrier_freq(sd, 1);
adv7175_write(sd, 0x0c, TR1CAPT); /* TR1 */
if (encoder->norm & V4L2_STD_SECAM)
adv7175_write(sd, 0x0d, 0x49); /* Disable genlock */
else
adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */
adv7175_write(sd, 0x07, TR0MODE | TR0RST);
adv7175_write(sd, 0x07, TR0MODE);
/*udelay(10);*/
break;
case 1:
adv7175_write(sd, 0x01, 0x00);
if (encoder->norm & V4L2_STD_NTSC)
set_subcarrier_freq(sd, 0);
adv7175_write(sd, 0x0c, TR1PLAY); /* TR1 */
adv7175_write(sd, 0x0d, 0x49);
adv7175_write(sd, 0x07, TR0MODE | TR0RST);
adv7175_write(sd, 0x07, TR0MODE);
/* udelay(10); */
break;
case 2:
adv7175_write(sd, 0x01, 0x80);
if (encoder->norm & V4L2_STD_NTSC)
set_subcarrier_freq(sd, 0);
adv7175_write(sd, 0x0d, 0x49);
adv7175_write(sd, 0x07, TR0MODE | TR0RST);
adv7175_write(sd, 0x07, TR0MODE);
/* udelay(10); */
break;
default:
v4l2_dbg(1, debug, sd, "illegal input: %d\n", input);
return -EINVAL;
}
v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[input]);
encoder->input = input;
return 0;
}
static int adv7175_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
enum v4l2_mbus_pixelcode *code)
{
if (index >= ARRAY_SIZE(adv7175_codes))
return -EINVAL;
*code = adv7175_codes[index];
return 0;
}
static int adv7175_g_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
u8 val = adv7175_read(sd, 0x7);
if ((val & 0x40) == (1 << 6))
mf->code = V4L2_MBUS_FMT_UYVY8_1X16;
else
mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
mf->width = 0;
mf->height = 0;
mf->field = V4L2_FIELD_ANY;
return 0;
}
static int adv7175_s_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
u8 val = adv7175_read(sd, 0x7);
int ret;
switch (mf->code) {
case V4L2_MBUS_FMT_UYVY8_2X8:
val &= ~0x40;
break;
case V4L2_MBUS_FMT_UYVY8_1X16:
val |= 0x40;
break;
default:
v4l2_dbg(1, debug, sd,
"illegal v4l2_mbus_framefmt code: %d\n", mf->code);
return -EINVAL;
}
ret = adv7175_write(sd, 0x7, val);
return ret;
}
static int adv7175_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7175, 0);
}
static int adv7175_s_power(struct v4l2_subdev *sd, int on)
{
if (on)
adv7175_write(sd, 0x01, 0x00);
else
adv7175_write(sd, 0x01, 0x78);
return 0;
}
/* ----------------------------------------------------------------------- */
static const struct v4l2_subdev_core_ops adv7175_core_ops = {
.g_chip_ident = adv7175_g_chip_ident,
.init = adv7175_init,
.s_power = adv7175_s_power,
};
static const struct v4l2_subdev_video_ops adv7175_video_ops = {
.s_std_output = adv7175_s_std_output,
.s_routing = adv7175_s_routing,
.s_mbus_fmt = adv7175_s_fmt,
.g_mbus_fmt = adv7175_g_fmt,
.enum_mbus_fmt = adv7175_enum_fmt,
};
static const struct v4l2_subdev_ops adv7175_ops = {
.core = &adv7175_core_ops,
.video = &adv7175_video_ops,
};
/* ----------------------------------------------------------------------- */
static int adv7175_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int i;
struct adv7175 *encoder;
struct v4l2_subdev *sd;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
encoder = kzalloc(sizeof(struct adv7175), GFP_KERNEL);
if (encoder == NULL)
return -ENOMEM;
sd = &encoder->sd;
v4l2_i2c_subdev_init(sd, client, &adv7175_ops);
encoder->norm = V4L2_STD_NTSC;
encoder->input = 0;
i = adv7175_write_block(sd, init_common, sizeof(init_common));
if (i >= 0) {
i = adv7175_write(sd, 0x07, TR0MODE | TR0RST);
i = adv7175_write(sd, 0x07, TR0MODE);
i = adv7175_read(sd, 0x12);
v4l2_dbg(1, debug, sd, "revision %d\n", i & 1);
}
if (i < 0)
v4l2_dbg(1, debug, sd, "init error 0x%x\n", i);
return 0;
}
static int adv7175_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(sd);
kfree(to_adv7175(sd));
return 0;
}
/* ----------------------------------------------------------------------- */
static const struct i2c_device_id adv7175_id[] = {
{ "adv7175", 0 },
{ "adv7176", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adv7175_id);
static struct i2c_driver adv7175_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "adv7175",
},
.probe = adv7175_probe,
.remove = adv7175_remove,
.id_table = adv7175_id,
};
module_i2c_driver(adv7175_driver);

667
drivers/media/i2c/adv7180.c Normal file
View file

@ -0,0 +1,667 @@
/*
* adv7180.c Analog Devices ADV7180 video decoder driver
* Copyright (c) 2009 Intel Corporation
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <media/v4l2-ioctl.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-chip-ident.h>
#include <linux/mutex.h>
#define ADV7180_INPUT_CONTROL_REG 0x00
#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM 0x00
#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM_PED 0x10
#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_J_SECAM 0x20
#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_M_SECAM 0x30
#define ADV7180_INPUT_CONTROL_NTSC_J 0x40
#define ADV7180_INPUT_CONTROL_NTSC_M 0x50
#define ADV7180_INPUT_CONTROL_PAL60 0x60
#define ADV7180_INPUT_CONTROL_NTSC_443 0x70
#define ADV7180_INPUT_CONTROL_PAL_BG 0x80
#define ADV7180_INPUT_CONTROL_PAL_N 0x90
#define ADV7180_INPUT_CONTROL_PAL_M 0xa0
#define ADV7180_INPUT_CONTROL_PAL_M_PED 0xb0
#define ADV7180_INPUT_CONTROL_PAL_COMB_N 0xc0
#define ADV7180_INPUT_CONTROL_PAL_COMB_N_PED 0xd0
#define ADV7180_INPUT_CONTROL_PAL_SECAM 0xe0
#define ADV7180_INPUT_CONTROL_PAL_SECAM_PED 0xf0
#define ADV7180_INPUT_CONTROL_INSEL_MASK 0x0f
#define ADV7180_EXTENDED_OUTPUT_CONTROL_REG 0x04
#define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5
#define ADV7180_AUTODETECT_ENABLE_REG 0x07
#define ADV7180_AUTODETECT_DEFAULT 0x7f
/* Contrast */
#define ADV7180_CON_REG 0x08 /*Unsigned */
#define ADV7180_CON_MIN 0
#define ADV7180_CON_DEF 128
#define ADV7180_CON_MAX 255
/* Brightness*/
#define ADV7180_BRI_REG 0x0a /*Signed */
#define ADV7180_BRI_MIN -128
#define ADV7180_BRI_DEF 0
#define ADV7180_BRI_MAX 127
/* Hue */
#define ADV7180_HUE_REG 0x0b /*Signed, inverted */
#define ADV7180_HUE_MIN -127
#define ADV7180_HUE_DEF 0
#define ADV7180_HUE_MAX 128
#define ADV7180_ADI_CTRL_REG 0x0e
#define ADV7180_ADI_CTRL_IRQ_SPACE 0x20
#define ADV7180_PWR_MAN_REG 0x0f
#define ADV7180_PWR_MAN_ON 0x04
#define ADV7180_PWR_MAN_OFF 0x24
#define ADV7180_PWR_MAN_RES 0x80
#define ADV7180_STATUS1_REG 0x10
#define ADV7180_STATUS1_IN_LOCK 0x01
#define ADV7180_STATUS1_AUTOD_MASK 0x70
#define ADV7180_STATUS1_AUTOD_NTSM_M_J 0x00
#define ADV7180_STATUS1_AUTOD_NTSC_4_43 0x10
#define ADV7180_STATUS1_AUTOD_PAL_M 0x20
#define ADV7180_STATUS1_AUTOD_PAL_60 0x30
#define ADV7180_STATUS1_AUTOD_PAL_B_G 0x40
#define ADV7180_STATUS1_AUTOD_SECAM 0x50
#define ADV7180_STATUS1_AUTOD_PAL_COMB 0x60
#define ADV7180_STATUS1_AUTOD_SECAM_525 0x70
#define ADV7180_IDENT_REG 0x11
#define ADV7180_ID_7180 0x18
#define ADV7180_ICONF1_ADI 0x40
#define ADV7180_ICONF1_ACTIVE_LOW 0x01
#define ADV7180_ICONF1_PSYNC_ONLY 0x10
#define ADV7180_ICONF1_ACTIVE_TO_CLR 0xC0
/* Saturation */
#define ADV7180_SD_SAT_CB_REG 0xe3 /*Unsigned */
#define ADV7180_SD_SAT_CR_REG 0xe4 /*Unsigned */
#define ADV7180_SAT_MIN 0
#define ADV7180_SAT_DEF 128
#define ADV7180_SAT_MAX 255
#define ADV7180_IRQ1_LOCK 0x01
#define ADV7180_IRQ1_UNLOCK 0x02
#define ADV7180_ISR1_ADI 0x42
#define ADV7180_ICR1_ADI 0x43
#define ADV7180_IMR1_ADI 0x44
#define ADV7180_IMR2_ADI 0x48
#define ADV7180_IRQ3_AD_CHANGE 0x08
#define ADV7180_ISR3_ADI 0x4A
#define ADV7180_ICR3_ADI 0x4B
#define ADV7180_IMR3_ADI 0x4C
#define ADV7180_IMR4_ADI 0x50
#define ADV7180_NTSC_V_BIT_END_REG 0xE6
#define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND 0x4F
struct adv7180_state {
struct v4l2_ctrl_handler ctrl_hdl;
struct v4l2_subdev sd;
struct work_struct work;
struct mutex mutex; /* mutual excl. when accessing chip */
int irq;
v4l2_std_id curr_norm;
bool autodetect;
u8 input;
};
#define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler, \
struct adv7180_state, \
ctrl_hdl)->sd)
static v4l2_std_id adv7180_std_to_v4l2(u8 status1)
{
switch (status1 & ADV7180_STATUS1_AUTOD_MASK) {
case ADV7180_STATUS1_AUTOD_NTSM_M_J:
return V4L2_STD_NTSC;
case ADV7180_STATUS1_AUTOD_NTSC_4_43:
return V4L2_STD_NTSC_443;
case ADV7180_STATUS1_AUTOD_PAL_M:
return V4L2_STD_PAL_M;
case ADV7180_STATUS1_AUTOD_PAL_60:
return V4L2_STD_PAL_60;
case ADV7180_STATUS1_AUTOD_PAL_B_G:
return V4L2_STD_PAL;
case ADV7180_STATUS1_AUTOD_SECAM:
return V4L2_STD_SECAM;
case ADV7180_STATUS1_AUTOD_PAL_COMB:
return V4L2_STD_PAL_Nc | V4L2_STD_PAL_N;
case ADV7180_STATUS1_AUTOD_SECAM_525:
return V4L2_STD_SECAM;
default:
return V4L2_STD_UNKNOWN;
}
}
static int v4l2_std_to_adv7180(v4l2_std_id std)
{
if (std == V4L2_STD_PAL_60)
return ADV7180_INPUT_CONTROL_PAL60;
if (std == V4L2_STD_NTSC_443)
return ADV7180_INPUT_CONTROL_NTSC_443;
if (std == V4L2_STD_PAL_N)
return ADV7180_INPUT_CONTROL_PAL_N;
if (std == V4L2_STD_PAL_M)
return ADV7180_INPUT_CONTROL_PAL_M;
if (std == V4L2_STD_PAL_Nc)
return ADV7180_INPUT_CONTROL_PAL_COMB_N;
if (std & V4L2_STD_PAL)
return ADV7180_INPUT_CONTROL_PAL_BG;
if (std & V4L2_STD_NTSC)
return ADV7180_INPUT_CONTROL_NTSC_M;
if (std & V4L2_STD_SECAM)
return ADV7180_INPUT_CONTROL_PAL_SECAM;
return -EINVAL;
}
static u32 adv7180_status_to_v4l2(u8 status1)
{
if (!(status1 & ADV7180_STATUS1_IN_LOCK))
return V4L2_IN_ST_NO_SIGNAL;
return 0;
}
static int __adv7180_status(struct i2c_client *client, u32 *status,
v4l2_std_id *std)
{
int status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG);
if (status1 < 0)
return status1;
if (status)
*status = adv7180_status_to_v4l2(status1);
if (std)
*std = adv7180_std_to_v4l2(status1);
return 0;
}
static inline struct adv7180_state *to_state(struct v4l2_subdev *sd)
{
return container_of(sd, struct adv7180_state, sd);
}
static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
{
struct adv7180_state *state = to_state(sd);
int err = mutex_lock_interruptible(&state->mutex);
if (err)
return err;
/* when we are interrupt driven we know the state */
if (!state->autodetect || state->irq > 0)
*std = state->curr_norm;
else
err = __adv7180_status(v4l2_get_subdevdata(sd), NULL, std);
mutex_unlock(&state->mutex);
return err;
}
static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input,
u32 output, u32 config)
{
struct adv7180_state *state = to_state(sd);
int ret = mutex_lock_interruptible(&state->mutex);
struct i2c_client *client = v4l2_get_subdevdata(sd);
if (ret)
return ret;
/* We cannot discriminate between LQFP and 40-pin LFCSP, so accept
* all inputs and let the card driver take care of validation
*/
if ((input & ADV7180_INPUT_CONTROL_INSEL_MASK) != input)
goto out;
ret = i2c_smbus_read_byte_data(client, ADV7180_INPUT_CONTROL_REG);
if (ret < 0)
goto out;
ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK;
ret = i2c_smbus_write_byte_data(client,
ADV7180_INPUT_CONTROL_REG, ret | input);
state->input = input;
out:
mutex_unlock(&state->mutex);
return ret;
}
static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status)
{
struct adv7180_state *state = to_state(sd);
int ret = mutex_lock_interruptible(&state->mutex);
if (ret)
return ret;
ret = __adv7180_status(v4l2_get_subdevdata(sd), status, NULL);
mutex_unlock(&state->mutex);
return ret;
}
static int adv7180_g_chip_ident(struct v4l2_subdev *sd,
struct v4l2_dbg_chip_ident *chip)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7180, 0);
}
static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
{
struct adv7180_state *state = to_state(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret = mutex_lock_interruptible(&state->mutex);
if (ret)
return ret;
/* all standards -> autodetect */
if (std == V4L2_STD_ALL) {
ret =
i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG,
ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM
| state->input);
if (ret < 0)
goto out;
__adv7180_status(client, NULL, &state->curr_norm);
state->autodetect = true;
} else {
ret = v4l2_std_to_adv7180(std);
if (ret < 0)
goto out;
ret = i2c_smbus_write_byte_data(client,
ADV7180_INPUT_CONTROL_REG,
ret | state->input);
if (ret < 0)
goto out;
state->curr_norm = std;
state->autodetect = false;
}
ret = 0;
out:
mutex_unlock(&state->mutex);
return ret;
}
static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = to_adv7180_sd(ctrl);
struct adv7180_state *state = to_state(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret = mutex_lock_interruptible(&state->mutex);
int val;
if (ret)
return ret;
val = ctrl->val;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
ret = i2c_smbus_write_byte_data(client, ADV7180_BRI_REG, val);
break;
case V4L2_CID_HUE:
/*Hue is inverted according to HSL chart */
ret = i2c_smbus_write_byte_data(client, ADV7180_HUE_REG, -val);
break;
case V4L2_CID_CONTRAST:
ret = i2c_smbus_write_byte_data(client, ADV7180_CON_REG, val);
break;
case V4L2_CID_SATURATION:
/*
*This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE
*Let's not confuse the user, everybody understands saturation
*/
ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CB_REG,
val);
if (ret < 0)
break;
ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CR_REG,
val);
break;
default:
ret = -EINVAL;
}
mutex_unlock(&state->mutex);
return ret;
}
static const struct v4l2_ctrl_ops adv7180_ctrl_ops = {
.s_ctrl = adv7180_s_ctrl,
};
static int adv7180_init_controls(struct adv7180_state *state)
{
v4l2_ctrl_handler_init(&state->ctrl_hdl, 4);
v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
V4L2_CID_BRIGHTNESS, ADV7180_BRI_MIN,
ADV7180_BRI_MAX, 1, ADV7180_BRI_DEF);
v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
V4L2_CID_CONTRAST, ADV7180_CON_MIN,
ADV7180_CON_MAX, 1, ADV7180_CON_DEF);
v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
V4L2_CID_SATURATION, ADV7180_SAT_MIN,
ADV7180_SAT_MAX, 1, ADV7180_SAT_DEF);
v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
V4L2_CID_HUE, ADV7180_HUE_MIN,
ADV7180_HUE_MAX, 1, ADV7180_HUE_DEF);
state->sd.ctrl_handler = &state->ctrl_hdl;
if (state->ctrl_hdl.error) {
int err = state->ctrl_hdl.error;
v4l2_ctrl_handler_free(&state->ctrl_hdl);
return err;
}
v4l2_ctrl_handler_setup(&state->ctrl_hdl);
return 0;
}
static void adv7180_exit_controls(struct adv7180_state *state)
{
v4l2_ctrl_handler_free(&state->ctrl_hdl);
}
static const struct v4l2_subdev_video_ops adv7180_video_ops = {
.querystd = adv7180_querystd,
.g_input_status = adv7180_g_input_status,
.s_routing = adv7180_s_routing,
};
static const struct v4l2_subdev_core_ops adv7180_core_ops = {
.g_chip_ident = adv7180_g_chip_ident,
.s_std = adv7180_s_std,
.queryctrl = v4l2_subdev_queryctrl,
.g_ctrl = v4l2_subdev_g_ctrl,
.s_ctrl = v4l2_subdev_s_ctrl,
};
static const struct v4l2_subdev_ops adv7180_ops = {
.core = &adv7180_core_ops,
.video = &adv7180_video_ops,
};
static void adv7180_work(struct work_struct *work)
{
struct adv7180_state *state = container_of(work, struct adv7180_state,
work);
struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
u8 isr3;
mutex_lock(&state->mutex);
i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
ADV7180_ADI_CTRL_IRQ_SPACE);
isr3 = i2c_smbus_read_byte_data(client, ADV7180_ISR3_ADI);
/* clear */
i2c_smbus_write_byte_data(client, ADV7180_ICR3_ADI, isr3);
i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, 0);
if (isr3 & ADV7180_IRQ3_AD_CHANGE && state->autodetect)
__adv7180_status(client, NULL, &state->curr_norm);
mutex_unlock(&state->mutex);
enable_irq(state->irq);
}
static irqreturn_t adv7180_irq(int irq, void *devid)
{
struct adv7180_state *state = devid;
schedule_work(&state->work);
disable_irq_nosync(state->irq);
return IRQ_HANDLED;
}
static int init_device(struct i2c_client *client, struct adv7180_state *state)
{
int ret;
/* Initialize adv7180 */
/* Enable autodetection */
if (state->autodetect) {
ret =
i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG,
ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM
| state->input);
if (ret < 0)
return ret;
ret =
i2c_smbus_write_byte_data(client,
ADV7180_AUTODETECT_ENABLE_REG,
ADV7180_AUTODETECT_DEFAULT);
if (ret < 0)
return ret;
} else {
ret = v4l2_std_to_adv7180(state->curr_norm);
if (ret < 0)
return ret;
ret =
i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG,
ret | state->input);
if (ret < 0)
return ret;
}
/* ITU-R BT.656-4 compatible */
ret = i2c_smbus_write_byte_data(client,
ADV7180_EXTENDED_OUTPUT_CONTROL_REG,
ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS);
if (ret < 0)
return ret;
/* Manually set V bit end position in NTSC mode */
ret = i2c_smbus_write_byte_data(client,
ADV7180_NTSC_V_BIT_END_REG,
ADV7180_NTSC_V_BIT_END_MANUAL_NVEND);
if (ret < 0)
return ret;
/* read current norm */
__adv7180_status(client, NULL, &state->curr_norm);
/* register for interrupts */
if (state->irq > 0) {
ret = request_irq(state->irq, adv7180_irq, 0, KBUILD_MODNAME,
state);
if (ret)
return ret;
ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
ADV7180_ADI_CTRL_IRQ_SPACE);
if (ret < 0)
return ret;
/* config the Interrupt pin to be active low */
ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI,
ADV7180_ICONF1_ACTIVE_LOW |
ADV7180_ICONF1_PSYNC_ONLY);
if (ret < 0)
return ret;
ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0);
if (ret < 0)
return ret;
ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0);
if (ret < 0)
return ret;
/* enable AD change interrupts interrupts */
ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI,
ADV7180_IRQ3_AD_CHANGE);
if (ret < 0)
return ret;
ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0);
if (ret < 0)
return ret;
ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
0);
if (ret < 0)
return ret;
}
return 0;
}
static __devinit int adv7180_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct adv7180_state *state;
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))
return -EIO;
v4l_info(client, "chip found @ 0x%02x (%s)\n",
client->addr, client->adapter->name);
state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL);
if (state == NULL) {
ret = -ENOMEM;
goto err;
}
state->irq = client->irq;
INIT_WORK(&state->work, adv7180_work);
mutex_init(&state->mutex);
state->autodetect = true;
state->input = 0;
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
ret = adv7180_init_controls(state);
if (ret)
goto err_unreg_subdev;
ret = init_device(client, state);
if (ret)
goto err_free_ctrl;
return 0;
err_free_ctrl:
adv7180_exit_controls(state);
err_unreg_subdev:
mutex_destroy(&state->mutex);
v4l2_device_unregister_subdev(sd);
kfree(state);
err:
printk(KERN_ERR KBUILD_MODNAME ": Failed to probe: %d\n", ret);
return ret;
}
static __devexit int adv7180_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct adv7180_state *state = to_state(sd);
if (state->irq > 0) {
free_irq(client->irq, state);
if (cancel_work_sync(&state->work)) {
/*
* Work was pending, therefore we need to enable
* IRQ here to balance the disable_irq() done in the
* interrupt handler.
*/
enable_irq(state->irq);
}
}
mutex_destroy(&state->mutex);
v4l2_device_unregister_subdev(sd);
kfree(to_state(sd));
return 0;
}
static const struct i2c_device_id adv7180_id[] = {
{KBUILD_MODNAME, 0},
{},
};
#ifdef CONFIG_PM
static int adv7180_suspend(struct i2c_client *client, pm_message_t state)
{
int ret;
ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG,
ADV7180_PWR_MAN_OFF);
if (ret < 0)
return ret;
return 0;
}
static int adv7180_resume(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct adv7180_state *state = to_state(sd);
int ret;
ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG,
ADV7180_PWR_MAN_ON);
if (ret < 0)
return ret;
ret = init_device(client, state);
if (ret < 0)
return ret;
return 0;
}
#endif
MODULE_DEVICE_TABLE(i2c, adv7180_id);
static struct i2c_driver adv7180_driver = {
.driver = {
.owner = THIS_MODULE,
.name = KBUILD_MODNAME,
},
.probe = adv7180_probe,
.remove = __devexit_p(adv7180_remove),
#ifdef CONFIG_PM
.suspend = adv7180_suspend,
.resume = adv7180_resume,
#endif
.id_table = adv7180_id,
};
module_i2c_driver(adv7180_driver);
MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver");
MODULE_AUTHOR("Mocean Laboratories");
MODULE_LICENSE("GPL v2");

699
drivers/media/i2c/adv7183.c Normal file
View file

@ -0,0 +1,699 @@
/*
* adv7183.c Analog Devices ADV7183 video decoder driver
*
* Copyright (c) 2011 Analog Devices 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/videodev2.h>
#include <media/adv7183.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include "adv7183_regs.h"
struct adv7183 {
struct v4l2_subdev sd;
struct v4l2_ctrl_handler hdl;
v4l2_std_id std; /* Current set standard */
u32 input;
u32 output;
unsigned reset_pin;
unsigned oe_pin;
struct v4l2_mbus_framefmt fmt;
};
/* EXAMPLES USING 27 MHz CLOCK
* Mode 1 CVBS Input (Composite Video on AIN5)
* All standards are supported through autodetect, 8-bit, 4:2:2, ITU-R BT.656 output on P15 to P8.
*/
static const unsigned char adv7183_init_regs[] = {
ADV7183_IN_CTRL, 0x04, /* CVBS input on AIN5 */
ADV7183_DIGI_CLAMP_CTRL_1, 0x00, /* Slow down digital clamps */
ADV7183_SHAP_FILT_CTRL, 0x41, /* Set CSFM to SH1 */
ADV7183_ADC_CTRL, 0x16, /* Power down ADC 1 and ADC 2 */
ADV7183_CTI_DNR_CTRL_4, 0x04, /* Set DNR threshold to 4 for flat response */
/* ADI recommended programming sequence */
ADV7183_ADI_CTRL, 0x80,
ADV7183_CTI_DNR_CTRL_4, 0x20,
0x52, 0x18,
0x58, 0xED,
0x77, 0xC5,
0x7C, 0x93,
0x7D, 0x00,
0xD0, 0x48,
0xD5, 0xA0,
0xD7, 0xEA,
ADV7183_SD_SATURATION_CR, 0x3E,
ADV7183_PAL_V_END, 0x3E,
ADV7183_PAL_F_TOGGLE, 0x0F,
ADV7183_ADI_CTRL, 0x00,
};
static inline struct adv7183 *to_adv7183(struct v4l2_subdev *sd)
{
return container_of(sd, struct adv7183, sd);
}
static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
{
return &container_of(ctrl->handler, struct adv7183, hdl)->sd;
}
static inline int adv7183_read(struct v4l2_subdev *sd, unsigned char reg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
return i2c_smbus_read_byte_data(client, reg);
}
static inline int adv7183_write(struct v4l2_subdev *sd, unsigned char reg,
unsigned char value)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
return i2c_smbus_write_byte_data(client, reg, value);
}
static int adv7183_writeregs(struct v4l2_subdev *sd,
const unsigned char *regs, unsigned int num)
{
unsigned char reg, data;
unsigned int cnt = 0;
if (num & 0x1) {
v4l2_err(sd, "invalid regs array\n");
return -1;
}
while (cnt < num) {
reg = *regs++;
data = *regs++;
cnt += 2;
adv7183_write(sd, reg, data);
}
return 0;
}
static int adv7183_log_status(struct v4l2_subdev *sd)
{
struct adv7183 *decoder = to_adv7183(sd);
v4l2_info(sd, "adv7183: Input control = 0x%02x\n",
adv7183_read(sd, ADV7183_IN_CTRL));
v4l2_info(sd, "adv7183: Video selection = 0x%02x\n",
adv7183_read(sd, ADV7183_VD_SEL));
v4l2_info(sd, "adv7183: Output control = 0x%02x\n",
adv7183_read(sd, ADV7183_OUT_CTRL));
v4l2_info(sd, "adv7183: Extended output control = 0x%02x\n",
adv7183_read(sd, ADV7183_EXT_OUT_CTRL));
v4l2_info(sd, "adv7183: Autodetect enable = 0x%02x\n",
adv7183_read(sd, ADV7183_AUTO_DET_EN));
v4l2_info(sd, "adv7183: Contrast = 0x%02x\n",
adv7183_read(sd, ADV7183_CONTRAST));
v4l2_info(sd, "adv7183: Brightness = 0x%02x\n",
adv7183_read(sd, ADV7183_BRIGHTNESS));
v4l2_info(sd, "adv7183: Hue = 0x%02x\n",
adv7183_read(sd, ADV7183_HUE));
v4l2_info(sd, "adv7183: Default value Y = 0x%02x\n",
adv7183_read(sd, ADV7183_DEF_Y));
v4l2_info(sd, "adv7183: Default value C = 0x%02x\n",
adv7183_read(sd, ADV7183_DEF_C));
v4l2_info(sd, "adv7183: ADI control = 0x%02x\n",
adv7183_read(sd, ADV7183_ADI_CTRL));
v4l2_info(sd, "adv7183: Power Management = 0x%02x\n",
adv7183_read(sd, ADV7183_POW_MANAGE));
v4l2_info(sd, "adv7183: Status 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",
adv7183_read(sd, ADV7183_STATUS_1),
adv7183_read(sd, ADV7183_STATUS_2),
adv7183_read(sd, ADV7183_STATUS_3));
v4l2_info(sd, "adv7183: Ident = 0x%02x\n",
adv7183_read(sd, ADV7183_IDENT));
v4l2_info(sd, "adv7183: Analog clamp control = 0x%02x\n",
adv7183_read(sd, ADV7183_ANAL_CLAMP_CTRL));
v4l2_info(sd, "adv7183: Digital clamp control 1 = 0x%02x\n",
adv7183_read(sd, ADV7183_DIGI_CLAMP_CTRL_1));
v4l2_info(sd, "adv7183: Shaping filter control 1 and 2 = 0x%02x 0x%02x\n",
adv7183_read(sd, ADV7183_SHAP_FILT_CTRL),
adv7183_read(sd, ADV7183_SHAP_FILT_CTRL_2));
v4l2_info(sd, "adv7183: Comb filter control = 0x%02x\n",
adv7183_read(sd, ADV7183_COMB_FILT_CTRL));
v4l2_info(sd, "adv7183: ADI control 2 = 0x%02x\n",
adv7183_read(sd, ADV7183_ADI_CTRL_2));
v4l2_info(sd, "adv7183: Pixel delay control = 0x%02x\n",
adv7183_read(sd, ADV7183_PIX_DELAY_CTRL));
v4l2_info(sd, "adv7183: Misc gain control = 0x%02x\n",
adv7183_read(sd, ADV7183_MISC_GAIN_CTRL));
v4l2_info(sd, "adv7183: AGC mode control = 0x%02x\n",
adv7183_read(sd, ADV7183_AGC_MODE_CTRL));
v4l2_info(sd, "adv7183: Chroma gain control 1 and 2 = 0x%02x 0x%02x\n",
adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_1),
adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_2));
v4l2_info(sd, "adv7183: Luma gain control 1 and 2 = 0x%02x 0x%02x\n",
adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_1),
adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_2));
v4l2_info(sd, "adv7183: Vsync field control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",
adv7183_read(sd, ADV7183_VS_FIELD_CTRL_1),
adv7183_read(sd, ADV7183_VS_FIELD_CTRL_2),
adv7183_read(sd, ADV7183_VS_FIELD_CTRL_3));
v4l2_info(sd, "adv7183: Hsync positon control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",
adv7183_read(sd, ADV7183_HS_POS_CTRL_1),
adv7183_read(sd, ADV7183_HS_POS_CTRL_2),
adv7183_read(sd, ADV7183_HS_POS_CTRL_3));
v4l2_info(sd, "adv7183: Polarity = 0x%02x\n",
adv7183_read(sd, ADV7183_POLARITY));
v4l2_info(sd, "adv7183: ADC control = 0x%02x\n",
adv7183_read(sd, ADV7183_ADC_CTRL));
v4l2_info(sd, "adv7183: SD offset Cb and Cr = 0x%02x 0x%02x\n",
adv7183_read(sd, ADV7183_SD_OFFSET_CB),
adv7183_read(sd, ADV7183_SD_OFFSET_CR));
v4l2_info(sd, "adv7183: SD saturation Cb and Cr = 0x%02x 0x%02x\n",
adv7183_read(sd, ADV7183_SD_SATURATION_CB),
adv7183_read(sd, ADV7183_SD_SATURATION_CR));
v4l2_info(sd, "adv7183: Drive strength = 0x%02x\n",
adv7183_read(sd, ADV7183_DRIVE_STR));
v4l2_ctrl_handler_log_status(&decoder->hdl, sd->name);
return 0;
}
static int adv7183_g_std(struct v4l2_subdev *sd, v4l2_std_id *std)
{
struct adv7183 *decoder = to_adv7183(sd);
*std = decoder->std;
return 0;
}
static int adv7183_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
{
struct adv7183 *decoder = to_adv7183(sd);
int reg;
reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF;
if (std == V4L2_STD_PAL_60)
reg |= 0x60;
else if (std == V4L2_STD_NTSC_443)
reg |= 0x70;
else if (std == V4L2_STD_PAL_N)
reg |= 0x90;
else if (std == V4L2_STD_PAL_M)
reg |= 0xA0;
else if (std == V4L2_STD_PAL_Nc)
reg |= 0xC0;
else if (std & V4L2_STD_PAL)
reg |= 0x80;
else if (std & V4L2_STD_NTSC)
reg |= 0x50;
else if (std & V4L2_STD_SECAM)
reg |= 0xE0;
else
return -EINVAL;
adv7183_write(sd, ADV7183_IN_CTRL, reg);
decoder->std = std;
return 0;
}
static int adv7183_reset(struct v4l2_subdev *sd, u32 val)
{
int reg;
reg = adv7183_read(sd, ADV7183_POW_MANAGE) | 0x80;
adv7183_write(sd, ADV7183_POW_MANAGE, reg);
/* wait 5ms before any further i2c writes are performed */
usleep_range(5000, 10000);
return 0;
}
static int adv7183_s_routing(struct v4l2_subdev *sd,
u32 input, u32 output, u32 config)
{
struct adv7183 *decoder = to_adv7183(sd);
int reg;
if ((input > ADV7183_COMPONENT1) || (output > ADV7183_16BIT_OUT))
return -EINVAL;
if (input != decoder->input) {
decoder->input = input;
reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF0;
switch (input) {
case ADV7183_COMPOSITE1:
reg |= 0x1;
break;
case ADV7183_COMPOSITE2:
reg |= 0x2;
break;
case ADV7183_COMPOSITE3:
reg |= 0x3;
break;
case ADV7183_COMPOSITE4:
reg |= 0x4;
break;
case ADV7183_COMPOSITE5:
reg |= 0x5;
break;
case ADV7183_COMPOSITE6:
reg |= 0xB;
break;
case ADV7183_COMPOSITE7:
reg |= 0xC;
break;
case ADV7183_COMPOSITE8:
reg |= 0xD;
break;
case ADV7183_COMPOSITE9:
reg |= 0xE;
break;
case ADV7183_COMPOSITE10:
reg |= 0xF;
break;
case ADV7183_SVIDEO0:
reg |= 0x6;
break;
case ADV7183_SVIDEO1:
reg |= 0x7;
break;
case ADV7183_SVIDEO2:
reg |= 0x8;
break;
case ADV7183_COMPONENT0:
reg |= 0x9;
break;
case ADV7183_COMPONENT1:
reg |= 0xA;
break;
default:
break;
}
adv7183_write(sd, ADV7183_IN_CTRL, reg);
}
if (output != decoder->output) {
decoder->output = output;
reg = adv7183_read(sd, ADV7183_OUT_CTRL) & 0xC0;
switch (output) {
case ADV7183_16BIT_OUT:
reg |= 0x9;
break;
default:
reg |= 0xC;
break;
}
adv7183_write(sd, ADV7183_OUT_CTRL, reg);
}
return 0;
}
static int adv7183_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = to_sd(ctrl);
int val = ctrl->val;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
if (val < 0)
val = 127 - val;
adv7183_write(sd, ADV7183_BRIGHTNESS, val);
break;
case V4L2_CID_CONTRAST:
adv7183_write(sd, ADV7183_CONTRAST, val);
break;
case V4L2_CID_SATURATION:
adv7183_write(sd, ADV7183_SD_SATURATION_CB, val >> 8);
adv7183_write(sd, ADV7183_SD_SATURATION_CR, (val & 0xFF));
break;
case V4L2_CID_HUE:
adv7183_write(sd, ADV7183_SD_OFFSET_CB, val >> 8);
adv7183_write(sd, ADV7183_SD_OFFSET_CR, (val & 0xFF));
break;
default:
return -EINVAL;
}
return 0;
}
static int adv7183_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
{
struct adv7183 *decoder = to_adv7183(sd);
int reg;
/* enable autodetection block */
reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF;
adv7183_write(sd, ADV7183_IN_CTRL, reg);
/* wait autodetection switch */
mdelay(10);
/* get autodetection result */
reg = adv7183_read(sd, ADV7183_STATUS_1);
switch ((reg >> 0x4) & 0x7) {
case 0:
*std = V4L2_STD_NTSC;
break;
case 1:
*std = V4L2_STD_NTSC_443;
break;
case 2:
*std = V4L2_STD_PAL_M;
break;
case 3:
*std = V4L2_STD_PAL_60;
break;
case 4:
*std = V4L2_STD_PAL;
break;
case 5:
*std = V4L2_STD_SECAM;
break;
case 6:
*std = V4L2_STD_PAL_Nc;
break;
case 7:
*std = V4L2_STD_SECAM;
break;
default:
*std = V4L2_STD_UNKNOWN;
break;
}
/* after std detection, write back user set std */
adv7183_s_std(sd, decoder->std);
return 0;
}
static int adv7183_g_input_status(struct v4l2_subdev *sd, u32 *status)
{
int reg;
*status = V4L2_IN_ST_NO_SIGNAL;
reg = adv7183_read(sd, ADV7183_STATUS_1);
if (reg < 0)
return reg;
if (reg & 0x1)
*status = 0;
return 0;
}
static int adv7183_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
enum v4l2_mbus_pixelcode *code)
{
if (index > 0)
return -EINVAL;
*code = V4L2_MBUS_FMT_UYVY8_2X8;
return 0;
}
static int adv7183_try_mbus_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *fmt)
{
struct adv7183 *decoder = to_adv7183(sd);
fmt->code = V4L2_MBUS_FMT_UYVY8_2X8;
fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
if (decoder->std & V4L2_STD_525_60) {
fmt->field = V4L2_FIELD_SEQ_TB;
fmt->width = 720;
fmt->height = 480;
} else {
fmt->field = V4L2_FIELD_SEQ_BT;
fmt->width = 720;
fmt->height = 576;
}
return 0;
}
static int adv7183_s_mbus_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *fmt)
{
struct adv7183 *decoder = to_adv7183(sd);
adv7183_try_mbus_fmt(sd, fmt);
decoder->fmt = *fmt;
return 0;
}
static int adv7183_g_mbus_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *fmt)
{
struct adv7183 *decoder = to_adv7183(sd);
*fmt = decoder->fmt;
return 0;
}
static int adv7183_s_stream(struct v4l2_subdev *sd, int enable)
{
struct adv7183 *decoder = to_adv7183(sd);
if (enable)
gpio_direction_output(decoder->oe_pin, 0);
else
gpio_direction_output(decoder->oe_pin, 1);
udelay(1);
return 0;
}
static int adv7183_g_chip_ident(struct v4l2_subdev *sd,
struct v4l2_dbg_chip_ident *chip)
{
int rev;
struct i2c_client *client = v4l2_get_subdevdata(sd);
/* 0x11 for adv7183, 0x13 for adv7183b */
rev = adv7183_read(sd, ADV7183_IDENT);
return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7183, rev);
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int adv7183_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, &reg->match))
return -EINVAL;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
reg->val = adv7183_read(sd, reg->reg & 0xff);
reg->size = 1;
return 0;
}
static int adv7183_s_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, &reg->match))
return -EINVAL;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
adv7183_write(sd, reg->reg & 0xff, reg->val & 0xff);
return 0;
}
#endif
static const struct v4l2_ctrl_ops adv7183_ctrl_ops = {
.s_ctrl = adv7183_s_ctrl,
};
static const struct v4l2_subdev_core_ops adv7183_core_ops = {
.log_status = adv7183_log_status,
.g_std = adv7183_g_std,
.s_std = adv7183_s_std,
.reset = adv7183_reset,
.g_chip_ident = adv7183_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = adv7183_g_register,
.s_register = adv7183_s_register,
#endif
};
static const struct v4l2_subdev_video_ops adv7183_video_ops = {
.s_routing = adv7183_s_routing,
.querystd = adv7183_querystd,
.g_input_status = adv7183_g_input_status,
.enum_mbus_fmt = adv7183_enum_mbus_fmt,
.try_mbus_fmt = adv7183_try_mbus_fmt,
.s_mbus_fmt = adv7183_s_mbus_fmt,
.g_mbus_fmt = adv7183_g_mbus_fmt,
.s_stream = adv7183_s_stream,
};
static const struct v4l2_subdev_ops adv7183_ops = {
.core = &adv7183_core_ops,
.video = &adv7183_video_ops,
};
static int adv7183_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct adv7183 *decoder;
struct v4l2_subdev *sd;
struct v4l2_ctrl_handler *hdl;
int ret;
struct v4l2_mbus_framefmt fmt;
const unsigned *pin_array;
/* 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);
pin_array = client->dev.platform_data;
if (pin_array == NULL)
return -EINVAL;
decoder = kzalloc(sizeof(struct adv7183), GFP_KERNEL);
if (decoder == NULL)
return -ENOMEM;
decoder->reset_pin = pin_array[0];
decoder->oe_pin = pin_array[1];
if (gpio_request(decoder->reset_pin, "ADV7183 Reset")) {
v4l_err(client, "failed to request GPIO %d\n", decoder->reset_pin);
ret = -EBUSY;
goto err_free_decoder;
}
if (gpio_request(decoder->oe_pin, "ADV7183 Output Enable")) {
v4l_err(client, "failed to request GPIO %d\n", decoder->oe_pin);
ret = -EBUSY;
goto err_free_reset;
}
sd = &decoder->sd;
v4l2_i2c_subdev_init(sd, client, &adv7183_ops);
hdl = &decoder->hdl;
v4l2_ctrl_handler_init(hdl, 4);
v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x80);
v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
V4L2_CID_SATURATION, 0, 0xFFFF, 1, 0x8080);
v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
V4L2_CID_HUE, 0, 0xFFFF, 1, 0x8080);
/* hook the control handler into the driver */
sd->ctrl_handler = hdl;
if (hdl->error) {
ret = hdl->error;
v4l2_ctrl_handler_free(hdl);
goto err_free_oe;
}
/* v4l2 doesn't support an autodetect standard, pick PAL as default */
decoder->std = V4L2_STD_PAL;
decoder->input = ADV7183_COMPOSITE4;
decoder->output = ADV7183_8BIT_OUT;
gpio_direction_output(decoder->oe_pin, 1);
/* reset chip */
gpio_direction_output(decoder->reset_pin, 0);
/* reset pulse width at least 5ms */
mdelay(10);
gpio_direction_output(decoder->reset_pin, 1);
/* wait 5ms before any further i2c writes are performed */
mdelay(5);
adv7183_writeregs(sd, adv7183_init_regs, ARRAY_SIZE(adv7183_init_regs));
adv7183_s_std(sd, decoder->std);
fmt.width = 720;
fmt.height = 576;
adv7183_s_mbus_fmt(sd, &fmt);
/* initialize the hardware to the default control values */
ret = v4l2_ctrl_handler_setup(hdl);
if (ret) {
v4l2_ctrl_handler_free(hdl);
goto err_free_oe;
}
return 0;
err_free_oe:
gpio_free(decoder->oe_pin);
err_free_reset:
gpio_free(decoder->reset_pin);
err_free_decoder:
kfree(decoder);
return ret;
}
static int adv7183_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct adv7183 *decoder = to_adv7183(sd);
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(sd->ctrl_handler);
gpio_free(decoder->oe_pin);
gpio_free(decoder->reset_pin);
kfree(decoder);
return 0;
}
static const struct i2c_device_id adv7183_id[] = {
{"adv7183", 0},
{},
};
MODULE_DEVICE_TABLE(i2c, adv7183_id);
static struct i2c_driver adv7183_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "adv7183",
},
.probe = adv7183_probe,
.remove = __devexit_p(adv7183_remove),
.id_table = adv7183_id,
};
static __init int adv7183_init(void)
{
return i2c_add_driver(&adv7183_driver);
}
static __exit void adv7183_exit(void)
{
i2c_del_driver(&adv7183_driver);
}
module_init(adv7183_init);
module_exit(adv7183_exit);
MODULE_DESCRIPTION("Analog Devices ADV7183 video decoder driver");
MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,107 @@
/*
* adv7183 - Analog Devices ADV7183 video decoder registers
*
* Copyright (c) 2011 Analog Devices 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _ADV7183_REGS_H_
#define _ADV7183_REGS_H_
#define ADV7183_IN_CTRL 0x00 /* Input control */
#define ADV7183_VD_SEL 0x01 /* Video selection */
#define ADV7183_OUT_CTRL 0x03 /* Output control */
#define ADV7183_EXT_OUT_CTRL 0x04 /* Extended output control */
#define ADV7183_AUTO_DET_EN 0x07 /* Autodetect enable */
#define ADV7183_CONTRAST 0x08 /* Contrast */
#define ADV7183_BRIGHTNESS 0x0A /* Brightness */
#define ADV7183_HUE 0x0B /* Hue */
#define ADV7183_DEF_Y 0x0C /* Default value Y */
#define ADV7183_DEF_C 0x0D /* Default value C */
#define ADV7183_ADI_CTRL 0x0E /* ADI control */
#define ADV7183_POW_MANAGE 0x0F /* Power Management */
#define ADV7183_STATUS_1 0x10 /* Status 1 */
#define ADV7183_IDENT 0x11 /* Ident */
#define ADV7183_STATUS_2 0x12 /* Status 2 */
#define ADV7183_STATUS_3 0x13 /* Status 3 */
#define ADV7183_ANAL_CLAMP_CTRL 0x14 /* Analog clamp control */
#define ADV7183_DIGI_CLAMP_CTRL_1 0x15 /* Digital clamp control 1 */
#define ADV7183_SHAP_FILT_CTRL 0x17 /* Shaping filter control */
#define ADV7183_SHAP_FILT_CTRL_2 0x18 /* Shaping filter control 2 */
#define ADV7183_COMB_FILT_CTRL 0x19 /* Comb filter control */
#define ADV7183_ADI_CTRL_2 0x1D /* ADI control 2 */
#define ADV7183_PIX_DELAY_CTRL 0x27 /* Pixel delay control */
#define ADV7183_MISC_GAIN_CTRL 0x2B /* Misc gain control */
#define ADV7183_AGC_MODE_CTRL 0x2C /* AGC mode control */
#define ADV7183_CHRO_GAIN_CTRL_1 0x2D /* Chroma gain control 1 */
#define ADV7183_CHRO_GAIN_CTRL_2 0x2E /* Chroma gain control 2 */
#define ADV7183_LUMA_GAIN_CTRL_1 0x2F /* Luma gain control 1 */
#define ADV7183_LUMA_GAIN_CTRL_2 0x30 /* Luma gain control 2 */
#define ADV7183_VS_FIELD_CTRL_1 0x31 /* Vsync field control 1 */
#define ADV7183_VS_FIELD_CTRL_2 0x32 /* Vsync field control 2 */
#define ADV7183_VS_FIELD_CTRL_3 0x33 /* Vsync field control 3 */
#define ADV7183_HS_POS_CTRL_1 0x34 /* Hsync positon control 1 */
#define ADV7183_HS_POS_CTRL_2 0x35 /* Hsync positon control 2 */
#define ADV7183_HS_POS_CTRL_3 0x36 /* Hsync positon control 3 */
#define ADV7183_POLARITY 0x37 /* Polarity */
#define ADV7183_NTSC_COMB_CTRL 0x38 /* NTSC comb control */
#define ADV7183_PAL_COMB_CTRL 0x39 /* PAL comb control */
#define ADV7183_ADC_CTRL 0x3A /* ADC control */
#define ADV7183_MAN_WIN_CTRL 0x3D /* Manual window control */
#define ADV7183_RESAMPLE_CTRL 0x41 /* Resample control */
#define ADV7183_GEMSTAR_CTRL_1 0x48 /* Gemstar ctrl 1 */
#define ADV7183_GEMSTAR_CTRL_2 0x49 /* Gemstar ctrl 2 */
#define ADV7183_GEMSTAR_CTRL_3 0x4A /* Gemstar ctrl 3 */
#define ADV7183_GEMSTAR_CTRL_4 0x4B /* Gemstar ctrl 4 */
#define ADV7183_GEMSTAR_CTRL_5 0x4C /* Gemstar ctrl 5 */
#define ADV7183_CTI_DNR_CTRL_1 0x4D /* CTI DNR ctrl 1 */
#define ADV7183_CTI_DNR_CTRL_2 0x4E /* CTI DNR ctrl 2 */
#define ADV7183_CTI_DNR_CTRL_4 0x50 /* CTI DNR ctrl 4 */
#define ADV7183_LOCK_CNT 0x51 /* Lock count */
#define ADV7183_FREE_LINE_LEN 0x8F /* Free-Run line length 1 */
#define ADV7183_VBI_INFO 0x90 /* VBI info */
#define ADV7183_WSS_1 0x91 /* WSS 1 */
#define ADV7183_WSS_2 0x92 /* WSS 2 */
#define ADV7183_EDTV_1 0x93 /* EDTV 1 */
#define ADV7183_EDTV_2 0x94 /* EDTV 2 */
#define ADV7183_EDTV_3 0x95 /* EDTV 3 */
#define ADV7183_CGMS_1 0x96 /* CGMS 1 */
#define ADV7183_CGMS_2 0x97 /* CGMS 2 */
#define ADV7183_CGMS_3 0x98 /* CGMS 3 */
#define ADV7183_CCAP_1 0x99 /* CCAP 1 */
#define ADV7183_CCAP_2 0x9A /* CCAP 2 */
#define ADV7183_LETTERBOX_1 0x9B /* Letterbox 1 */
#define ADV7183_LETTERBOX_2 0x9C /* Letterbox 2 */
#define ADV7183_LETTERBOX_3 0x9D /* Letterbox 3 */
#define ADV7183_CRC_EN 0xB2 /* CRC enable */
#define ADV7183_ADC_SWITCH_1 0xC3 /* ADC switch 1 */
#define ADV7183_ADC_SWITCH_2 0xC4 /* ADC swithc 2 */
#define ADV7183_LETTERBOX_CTRL_1 0xDC /* Letterbox control 1 */
#define ADV7183_LETTERBOX_CTRL_2 0xDD /* Letterbox control 2 */
#define ADV7183_SD_OFFSET_CB 0xE1 /* SD offset Cb */
#define ADV7183_SD_OFFSET_CR 0xE2 /* SD offset Cr */
#define ADV7183_SD_SATURATION_CB 0xE3 /* SD saturation Cb */
#define ADV7183_SD_SATURATION_CR 0xE4 /* SD saturation Cr */
#define ADV7183_NTSC_V_BEGIN 0xE5 /* NTSC V bit begin */
#define ADV7183_NTSC_V_END 0xE6 /* NTSC V bit end */
#define ADV7183_NTSC_F_TOGGLE 0xE7 /* NTSC F bit toggle */
#define ADV7183_PAL_V_BEGIN 0xE8 /* PAL V bit begin */
#define ADV7183_PAL_V_END 0xE9 /* PAL V bit end */
#define ADV7183_PAL_F_TOGGLE 0xEA /* PAL F bit toggle */
#define ADV7183_DRIVE_STR 0xF4 /* Drive strength */
#define ADV7183_IF_COMP_CTRL 0xF8 /* IF comp control */
#define ADV7183_VS_MODE_CTRL 0xF9 /* VS mode control */
#endif

476
drivers/media/i2c/adv7343.c Normal file
View file

@ -0,0 +1,476 @@
/*
* adv7343 - ADV7343 Video Encoder Driver
*
* The encoder hardware does not support SECAM.
*
* Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed .as is. WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* 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/videodev2.h>
#include <linux/uaccess.h>
#include <media/adv7343.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
#include "adv7343_regs.h"
MODULE_DESCRIPTION("ADV7343 video encoder driver");
MODULE_LICENSE("GPL");
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug level 0-1");
struct adv7343_state {
struct v4l2_subdev sd;
struct v4l2_ctrl_handler hdl;
u8 reg00;
u8 reg01;
u8 reg02;
u8 reg35;
u8 reg80;
u8 reg82;
u32 output;
v4l2_std_id std;
};
static inline struct adv7343_state *to_state(struct v4l2_subdev *sd)
{
return container_of(sd, struct adv7343_state, sd);
}
static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
{
return &container_of(ctrl->handler, struct adv7343_state, hdl)->sd;
}
static inline int adv7343_write(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 const u8 adv7343_init_reg_val[] = {
ADV7343_SOFT_RESET, ADV7343_SOFT_RESET_DEFAULT,
ADV7343_POWER_MODE_REG, ADV7343_POWER_MODE_REG_DEFAULT,
ADV7343_HD_MODE_REG1, ADV7343_HD_MODE_REG1_DEFAULT,
ADV7343_HD_MODE_REG2, ADV7343_HD_MODE_REG2_DEFAULT,
ADV7343_HD_MODE_REG3, ADV7343_HD_MODE_REG3_DEFAULT,
ADV7343_HD_MODE_REG4, ADV7343_HD_MODE_REG4_DEFAULT,
ADV7343_HD_MODE_REG5, ADV7343_HD_MODE_REG5_DEFAULT,
ADV7343_HD_MODE_REG6, ADV7343_HD_MODE_REG6_DEFAULT,
ADV7343_HD_MODE_REG7, ADV7343_HD_MODE_REG7_DEFAULT,
ADV7343_SD_MODE_REG1, ADV7343_SD_MODE_REG1_DEFAULT,
ADV7343_SD_MODE_REG2, ADV7343_SD_MODE_REG2_DEFAULT,
ADV7343_SD_MODE_REG3, ADV7343_SD_MODE_REG3_DEFAULT,
ADV7343_SD_MODE_REG4, ADV7343_SD_MODE_REG4_DEFAULT,
ADV7343_SD_MODE_REG5, ADV7343_SD_MODE_REG5_DEFAULT,
ADV7343_SD_MODE_REG6, ADV7343_SD_MODE_REG6_DEFAULT,
ADV7343_SD_MODE_REG7, ADV7343_SD_MODE_REG7_DEFAULT,
ADV7343_SD_MODE_REG8, ADV7343_SD_MODE_REG8_DEFAULT,
ADV7343_SD_HUE_REG, ADV7343_SD_HUE_REG_DEFAULT,
ADV7343_SD_CGMS_WSS0, ADV7343_SD_CGMS_WSS0_DEFAULT,
ADV7343_SD_BRIGHTNESS_WSS, ADV7343_SD_BRIGHTNESS_WSS_DEFAULT,
};
/*
* 2^32
* FSC(reg) = FSC (HZ) * --------
* 27000000
*/
static const struct adv7343_std_info stdinfo[] = {
{
/* FSC(Hz) = 3,579,545.45 Hz */
SD_STD_NTSC, 569408542, V4L2_STD_NTSC,
}, {
/* FSC(Hz) = 3,575,611.00 Hz */
SD_STD_PAL_M, 568782678, V4L2_STD_PAL_M,
}, {
/* FSC(Hz) = 3,582,056.00 */
SD_STD_PAL_N, 569807903, V4L2_STD_PAL_Nc,
}, {
/* FSC(Hz) = 4,433,618.75 Hz */
SD_STD_PAL_N, 705268427, V4L2_STD_PAL_N,
}, {
/* FSC(Hz) = 4,433,618.75 Hz */
SD_STD_PAL_BDGHI, 705268427, V4L2_STD_PAL,
}, {
/* FSC(Hz) = 4,433,618.75 Hz */
SD_STD_NTSC, 705268427, V4L2_STD_NTSC_443,
}, {
/* FSC(Hz) = 4,433,618.75 Hz */
SD_STD_PAL_M, 705268427, V4L2_STD_PAL_60,
},
};
static int adv7343_setstd(struct v4l2_subdev *sd, v4l2_std_id std)
{
struct adv7343_state *state = to_state(sd);
struct adv7343_std_info *std_info;
int num_std;
char *fsc_ptr;
u8 reg, val;
int err = 0;
int i = 0;
std_info = (struct adv7343_std_info *)stdinfo;
num_std = ARRAY_SIZE(stdinfo);
for (i = 0; i < num_std; i++) {
if (std_info[i].stdid & std)
break;
}
if (i == num_std) {
v4l2_dbg(1, debug, sd,
"Invalid std or std is not supported: %llx\n",
(unsigned long long)std);
return -EINVAL;
}
/* Set the standard */
val = state->reg80 & (~(SD_STD_MASK));
val |= std_info[i].standard_val3;
err = adv7343_write(sd, ADV7343_SD_MODE_REG1, val);
if (err < 0)
goto setstd_exit;
state->reg80 = val;
/* Configure the input mode register */
val = state->reg01 & (~((u8) INPUT_MODE_MASK));
val |= SD_INPUT_MODE;
err = adv7343_write(sd, ADV7343_MODE_SELECT_REG, val);
if (err < 0)
goto setstd_exit;
state->reg01 = val;
/* Program the sub carrier frequency registers */
fsc_ptr = (unsigned char *)&std_info[i].fsc_val;
reg = ADV7343_FSC_REG0;
for (i = 0; i < 4; i++, reg++, fsc_ptr++) {
err = adv7343_write(sd, reg, *fsc_ptr);
if (err < 0)
goto setstd_exit;
}
val = state->reg80;
/* Filter settings */
if (std & (V4L2_STD_NTSC | V4L2_STD_NTSC_443))
val &= 0x03;
else if (std & ~V4L2_STD_SECAM)
val |= 0x04;
err = adv7343_write(sd, ADV7343_SD_MODE_REG1, val);
if (err < 0)
goto setstd_exit;
state->reg80 = val;
setstd_exit:
if (err != 0)
v4l2_err(sd, "Error setting std, write failed\n");
return err;
}
static int adv7343_setoutput(struct v4l2_subdev *sd, u32 output_type)
{
struct adv7343_state *state = to_state(sd);
unsigned char val;
int err = 0;
if (output_type > ADV7343_SVIDEO_ID) {
v4l2_dbg(1, debug, sd,
"Invalid output type or output type not supported:%d\n",
output_type);
return -EINVAL;
}
/* Enable Appropriate DAC */
val = state->reg00 & 0x03;
if (output_type == ADV7343_COMPOSITE_ID)
val |= ADV7343_COMPOSITE_POWER_VALUE;
else if (output_type == ADV7343_COMPONENT_ID)
val |= ADV7343_COMPONENT_POWER_VALUE;
else
val |= ADV7343_SVIDEO_POWER_VALUE;
err = adv7343_write(sd, ADV7343_POWER_MODE_REG, val);
if (err < 0)
goto setoutput_exit;
state->reg00 = val;
/* Enable YUV output */
val = state->reg02 | YUV_OUTPUT_SELECT;
err = adv7343_write(sd, ADV7343_MODE_REG0, val);
if (err < 0)
goto setoutput_exit;
state->reg02 = val;
/* configure SD DAC Output 2 and SD DAC Output 1 bit to zero */
val = state->reg82 & (SD_DAC_1_DI & SD_DAC_2_DI);
err = adv7343_write(sd, ADV7343_SD_MODE_REG2, val);
if (err < 0)
goto setoutput_exit;
state->reg82 = val;
/* configure ED/HD Color DAC Swap and ED/HD RGB Input Enable bit to
* zero */
val = state->reg35 & (HD_RGB_INPUT_DI & HD_DAC_SWAP_DI);
err = adv7343_write(sd, ADV7343_HD_MODE_REG6, val);
if (err < 0)
goto setoutput_exit;
state->reg35 = val;
setoutput_exit:
if (err != 0)
v4l2_err(sd, "Error setting output, write failed\n");
return err;
}
static int adv7343_log_status(struct v4l2_subdev *sd)
{
struct adv7343_state *state = to_state(sd);
v4l2_info(sd, "Standard: %llx\n", (unsigned long long)state->std);
v4l2_info(sd, "Output: %s\n", (state->output == 0) ? "Composite" :
((state->output == 1) ? "Component" : "S-Video"));
return 0;
}
static int adv7343_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = to_sd(ctrl);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
return adv7343_write(sd, ADV7343_SD_BRIGHTNESS_WSS,
ctrl->val);
case V4L2_CID_HUE:
return adv7343_write(sd, ADV7343_SD_HUE_REG, ctrl->val);
case V4L2_CID_GAIN:
return adv7343_write(sd, ADV7343_DAC2_OUTPUT_LEVEL, ctrl->val);
}
return -EINVAL;
}
static int adv7343_g_chip_ident(struct v4l2_subdev *sd,
struct v4l2_dbg_chip_ident *chip)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7343, 0);
}
static const struct v4l2_ctrl_ops adv7343_ctrl_ops = {
.s_ctrl = adv7343_s_ctrl,
};
static const struct v4l2_subdev_core_ops adv7343_core_ops = {
.log_status = adv7343_log_status,
.g_chip_ident = adv7343_g_chip_ident,
.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 int adv7343_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
{
struct adv7343_state *state = to_state(sd);
int err = 0;
if (state->std == std)
return 0;
err = adv7343_setstd(sd, std);
if (!err)
state->std = std;
return err;
}
static int adv7343_s_routing(struct v4l2_subdev *sd,
u32 input, u32 output, u32 config)
{
struct adv7343_state *state = to_state(sd);
int err = 0;
if (state->output == output)
return 0;
err = adv7343_setoutput(sd, output);
if (!err)
state->output = output;
return err;
}
static const struct v4l2_subdev_video_ops adv7343_video_ops = {
.s_std_output = adv7343_s_std_output,
.s_routing = adv7343_s_routing,
};
static const struct v4l2_subdev_ops adv7343_ops = {
.core = &adv7343_core_ops,
.video = &adv7343_video_ops,
};
static int adv7343_initialize(struct v4l2_subdev *sd)
{
struct adv7343_state *state = to_state(sd);
int err = 0;
int i;
for (i = 0; i < ARRAY_SIZE(adv7343_init_reg_val); i += 2) {
err = adv7343_write(sd, adv7343_init_reg_val[i],
adv7343_init_reg_val[i+1]);
if (err) {
v4l2_err(sd, "Error initializing\n");
return err;
}
}
/* Configure for default video standard */
err = adv7343_setoutput(sd, state->output);
if (err < 0) {
v4l2_err(sd, "Error setting output during init\n");
return -EINVAL;
}
err = adv7343_setstd(sd, state->std);
if (err < 0) {
v4l2_err(sd, "Error setting std during init\n");
return -EINVAL;
}
return err;
}
static int adv7343_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct adv7343_state *state;
int err;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
state = kzalloc(sizeof(struct adv7343_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
state->reg00 = 0x80;
state->reg01 = 0x00;
state->reg02 = 0x20;
state->reg35 = 0x00;
state->reg80 = ADV7343_SD_MODE_REG1_DEFAULT;
state->reg82 = ADV7343_SD_MODE_REG2_DEFAULT;
state->output = ADV7343_COMPOSITE_ID;
state->std = V4L2_STD_NTSC;
v4l2_i2c_subdev_init(&state->sd, client, &adv7343_ops);
v4l2_ctrl_handler_init(&state->hdl, 2);
v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops,
V4L2_CID_BRIGHTNESS, ADV7343_BRIGHTNESS_MIN,
ADV7343_BRIGHTNESS_MAX, 1,
ADV7343_BRIGHTNESS_DEF);
v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops,
V4L2_CID_HUE, ADV7343_HUE_MIN,
ADV7343_HUE_MAX, 1,
ADV7343_HUE_DEF);
v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops,
V4L2_CID_GAIN, ADV7343_GAIN_MIN,
ADV7343_GAIN_MAX, 1,
ADV7343_GAIN_DEF);
state->sd.ctrl_handler = &state->hdl;
if (state->hdl.error) {
int err = state->hdl.error;
v4l2_ctrl_handler_free(&state->hdl);
kfree(state);
return err;
}
v4l2_ctrl_handler_setup(&state->hdl);
err = adv7343_initialize(&state->sd);
if (err) {
v4l2_ctrl_handler_free(&state->hdl);
kfree(state);
}
return err;
}
static int adv7343_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct adv7343_state *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 adv7343_id[] = {
{"adv7343", 0},
{},
};
MODULE_DEVICE_TABLE(i2c, adv7343_id);
static struct i2c_driver adv7343_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "adv7343",
},
.probe = adv7343_probe,
.remove = adv7343_remove,
.id_table = adv7343_id,
};
module_i2c_driver(adv7343_driver);

View file

@ -0,0 +1,181 @@
/*
* ADV7343 encoder related structure and register definitions
*
* Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed .as is. WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef ADV7343_REG_H
#define ADV7343_REGS_H
struct adv7343_std_info {
u32 standard_val3;
u32 fsc_val;
v4l2_std_id stdid;
};
/* Register offset macros */
#define ADV7343_POWER_MODE_REG (0x00)
#define ADV7343_MODE_SELECT_REG (0x01)
#define ADV7343_MODE_REG0 (0x02)
#define ADV7343_DAC2_OUTPUT_LEVEL (0x0b)
#define ADV7343_SOFT_RESET (0x17)
#define ADV7343_HD_MODE_REG1 (0x30)
#define ADV7343_HD_MODE_REG2 (0x31)
#define ADV7343_HD_MODE_REG3 (0x32)
#define ADV7343_HD_MODE_REG4 (0x33)
#define ADV7343_HD_MODE_REG5 (0x34)
#define ADV7343_HD_MODE_REG6 (0x35)
#define ADV7343_HD_MODE_REG7 (0x39)
#define ADV7343_SD_MODE_REG1 (0x80)
#define ADV7343_SD_MODE_REG2 (0x82)
#define ADV7343_SD_MODE_REG3 (0x83)
#define ADV7343_SD_MODE_REG4 (0x84)
#define ADV7343_SD_MODE_REG5 (0x86)
#define ADV7343_SD_MODE_REG6 (0x87)
#define ADV7343_SD_MODE_REG7 (0x88)
#define ADV7343_SD_MODE_REG8 (0x89)
#define ADV7343_FSC_REG0 (0x8C)
#define ADV7343_FSC_REG1 (0x8D)
#define ADV7343_FSC_REG2 (0x8E)
#define ADV7343_FSC_REG3 (0x8F)
#define ADV7343_SD_CGMS_WSS0 (0x99)
#define ADV7343_SD_HUE_REG (0xA0)
#define ADV7343_SD_BRIGHTNESS_WSS (0xA1)
/* Default values for the registers */
#define ADV7343_POWER_MODE_REG_DEFAULT (0x10)
#define ADV7343_HD_MODE_REG1_DEFAULT (0x3C) /* Changed Default
720p EAVSAV code*/
#define ADV7343_HD_MODE_REG2_DEFAULT (0x01) /* Changed Pixel data
valid */
#define ADV7343_HD_MODE_REG3_DEFAULT (0x00) /* Color delay 0 clks */
#define ADV7343_HD_MODE_REG4_DEFAULT (0xE8) /* Changed */
#define ADV7343_HD_MODE_REG5_DEFAULT (0x08)
#define ADV7343_HD_MODE_REG6_DEFAULT (0x00)
#define ADV7343_HD_MODE_REG7_DEFAULT (0x00)
#define ADV7343_SD_MODE_REG8_DEFAULT (0x00)
#define ADV7343_SOFT_RESET_DEFAULT (0x02)
#define ADV7343_COMPOSITE_POWER_VALUE (0x80)
#define ADV7343_COMPONENT_POWER_VALUE (0x1C)
#define ADV7343_SVIDEO_POWER_VALUE (0x60)
#define ADV7343_SD_HUE_REG_DEFAULT (127)
#define ADV7343_SD_BRIGHTNESS_WSS_DEFAULT (0x03)
#define ADV7343_SD_CGMS_WSS0_DEFAULT (0x10)
#define ADV7343_SD_MODE_REG1_DEFAULT (0x00)
#define ADV7343_SD_MODE_REG2_DEFAULT (0xC9)
#define ADV7343_SD_MODE_REG3_DEFAULT (0x10)
#define ADV7343_SD_MODE_REG4_DEFAULT (0x01)
#define ADV7343_SD_MODE_REG5_DEFAULT (0x02)
#define ADV7343_SD_MODE_REG6_DEFAULT (0x0C)
#define ADV7343_SD_MODE_REG7_DEFAULT (0x04)
#define ADV7343_SD_MODE_REG8_DEFAULT (0x00)
/* Bit masks for Mode Select Register */
#define INPUT_MODE_MASK (0x70)
#define SD_INPUT_MODE (0x00)
#define HD_720P_INPUT_MODE (0x10)
#define HD_1080I_INPUT_MODE (0x10)
/* Bit masks for Mode Register 0 */
#define TEST_PATTERN_BLACK_BAR_EN (0x04)
#define YUV_OUTPUT_SELECT (0x20)
#define RGB_OUTPUT_SELECT (0xDF)
/* Bit masks for DAC output levels */
#define DAC_OUTPUT_LEVEL_MASK (0xFF)
/* Bit masks for soft reset register */
#define SOFT_RESET (0x02)
/* Bit masks for HD Mode Register 1 */
#define OUTPUT_STD_MASK (0x03)
#define OUTPUT_STD_SHIFT (0)
#define OUTPUT_STD_EIA0_2 (0x00)
#define OUTPUT_STD_EIA0_1 (0x01)
#define OUTPUT_STD_FULL (0x02)
#define EMBEDDED_SYNC (0x04)
#define EXTERNAL_SYNC (0xFB)
#define STD_MODE_SHIFT (3)
#define STD_MODE_MASK (0x1F)
#define STD_MODE_720P (0x05)
#define STD_MODE_720P_25 (0x08)
#define STD_MODE_720P_30 (0x07)
#define STD_MODE_720P_50 (0x06)
#define STD_MODE_1080I (0x0D)
#define STD_MODE_1080I_25fps (0x0E)
#define STD_MODE_1080P_24 (0x12)
#define STD_MODE_1080P_25 (0x10)
#define STD_MODE_1080P_30 (0x0F)
#define STD_MODE_525P (0x00)
#define STD_MODE_625P (0x03)
/* Bit masks for SD Mode Register 1 */
#define SD_STD_MASK (0x03)
#define SD_STD_NTSC (0x00)
#define SD_STD_PAL_BDGHI (0x01)
#define SD_STD_PAL_M (0x02)
#define SD_STD_PAL_N (0x03)
#define SD_LUMA_FLTR_MASK (0x7)
#define SD_LUMA_FLTR_SHIFT (0x2)
#define SD_CHROMA_FLTR_MASK (0x7)
#define SD_CHROMA_FLTR_SHIFT (0x5)
/* Bit masks for SD Mode Register 2 */
#define SD_PBPR_SSAF_EN (0x01)
#define SD_PBPR_SSAF_DI (0xFE)
#define SD_DAC_1_DI (0xFD)
#define SD_DAC_2_DI (0xFB)
#define SD_PEDESTAL_EN (0x08)
#define SD_PEDESTAL_DI (0xF7)
#define SD_SQUARE_PIXEL_EN (0x10)
#define SD_SQUARE_PIXEL_DI (0xEF)
#define SD_PIXEL_DATA_VALID (0x40)
#define SD_ACTIVE_EDGE_EN (0x80)
#define SD_ACTIVE_EDGE_DI (0x7F)
/* Bit masks for HD Mode Register 6 */
#define HD_RGB_INPUT_EN (0x02)
#define HD_RGB_INPUT_DI (0xFD)
#define HD_PBPR_SYNC_EN (0x04)
#define HD_PBPR_SYNC_DI (0xFB)
#define HD_DAC_SWAP_EN (0x08)
#define HD_DAC_SWAP_DI (0xF7)
#define HD_GAMMA_CURVE_A (0xEF)
#define HD_GAMMA_CURVE_B (0x10)
#define HD_GAMMA_EN (0x20)
#define HD_GAMMA_DI (0xDF)
#define HD_ADPT_FLTR_MODEB (0x40)
#define HD_ADPT_FLTR_MODEA (0xBF)
#define HD_ADPT_FLTR_EN (0x80)
#define HD_ADPT_FLTR_DI (0x7F)
#define ADV7343_BRIGHTNESS_MAX (127)
#define ADV7343_BRIGHTNESS_MIN (0)
#define ADV7343_BRIGHTNESS_DEF (3)
#define ADV7343_HUE_MAX (255)
#define ADV7343_HUE_MIN (0)
#define ADV7343_HUE_DEF (127)
#define ADV7343_GAIN_MAX (64)
#define ADV7343_GAIN_MIN (-64)
#define ADV7343_GAIN_DEF (0)
#endif

487
drivers/media/i2c/adv7393.c Normal file
View file

@ -0,0 +1,487 @@
/*
* adv7393 - ADV7393 Video Encoder Driver
*
* The encoder hardware does not support SECAM.
*
* Copyright (C) 2010-2012 ADVANSEE - http://www.advansee.com/
* Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
*
* Based on ADV7343 driver,
*
* Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed .as is. WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* 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/videodev2.h>
#include <linux/uaccess.h>
#include <media/adv7393.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
#include "adv7393_regs.h"
MODULE_DESCRIPTION("ADV7393 video encoder driver");
MODULE_LICENSE("GPL");
static bool debug;
module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "Debug level 0-1");
struct adv7393_state {
struct v4l2_subdev sd;
struct v4l2_ctrl_handler hdl;
u8 reg00;
u8 reg01;
u8 reg02;
u8 reg35;
u8 reg80;
u8 reg82;
u32 output;
v4l2_std_id std;
};
static inline struct adv7393_state *to_state(struct v4l2_subdev *sd)
{
return container_of(sd, struct adv7393_state, sd);
}
static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
{
return &container_of(ctrl->handler, struct adv7393_state, hdl)->sd;
}
static inline int adv7393_write(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 const u8 adv7393_init_reg_val[] = {
ADV7393_SOFT_RESET, ADV7393_SOFT_RESET_DEFAULT,
ADV7393_POWER_MODE_REG, ADV7393_POWER_MODE_REG_DEFAULT,
ADV7393_HD_MODE_REG1, ADV7393_HD_MODE_REG1_DEFAULT,
ADV7393_HD_MODE_REG2, ADV7393_HD_MODE_REG2_DEFAULT,
ADV7393_HD_MODE_REG3, ADV7393_HD_MODE_REG3_DEFAULT,
ADV7393_HD_MODE_REG4, ADV7393_HD_MODE_REG4_DEFAULT,
ADV7393_HD_MODE_REG5, ADV7393_HD_MODE_REG5_DEFAULT,
ADV7393_HD_MODE_REG6, ADV7393_HD_MODE_REG6_DEFAULT,
ADV7393_HD_MODE_REG7, ADV7393_HD_MODE_REG7_DEFAULT,
ADV7393_SD_MODE_REG1, ADV7393_SD_MODE_REG1_DEFAULT,
ADV7393_SD_MODE_REG2, ADV7393_SD_MODE_REG2_DEFAULT,
ADV7393_SD_MODE_REG3, ADV7393_SD_MODE_REG3_DEFAULT,
ADV7393_SD_MODE_REG4, ADV7393_SD_MODE_REG4_DEFAULT,
ADV7393_SD_MODE_REG5, ADV7393_SD_MODE_REG5_DEFAULT,
ADV7393_SD_MODE_REG6, ADV7393_SD_MODE_REG6_DEFAULT,
ADV7393_SD_MODE_REG7, ADV7393_SD_MODE_REG7_DEFAULT,
ADV7393_SD_MODE_REG8, ADV7393_SD_MODE_REG8_DEFAULT,
ADV7393_SD_TIMING_REG0, ADV7393_SD_TIMING_REG0_DEFAULT,
ADV7393_SD_HUE_ADJUST, ADV7393_SD_HUE_ADJUST_DEFAULT,
ADV7393_SD_CGMS_WSS0, ADV7393_SD_CGMS_WSS0_DEFAULT,
ADV7393_SD_BRIGHTNESS_WSS, ADV7393_SD_BRIGHTNESS_WSS_DEFAULT,
};
/*
* 2^32
* FSC(reg) = FSC (HZ) * --------
* 27000000
*/
static const struct adv7393_std_info stdinfo[] = {
{
/* FSC(Hz) = 4,433,618.75 Hz */
SD_STD_NTSC, 705268427, V4L2_STD_NTSC_443,
}, {
/* FSC(Hz) = 3,579,545.45 Hz */
SD_STD_NTSC, 569408542, V4L2_STD_NTSC,
}, {
/* FSC(Hz) = 3,575,611.00 Hz */
SD_STD_PAL_M, 568782678, V4L2_STD_PAL_M,
}, {
/* FSC(Hz) = 3,582,056.00 Hz */
SD_STD_PAL_N, 569807903, V4L2_STD_PAL_Nc,
}, {
/* FSC(Hz) = 4,433,618.75 Hz */
SD_STD_PAL_N, 705268427, V4L2_STD_PAL_N,
}, {
/* FSC(Hz) = 4,433,618.75 Hz */
SD_STD_PAL_M, 705268427, V4L2_STD_PAL_60,
}, {
/* FSC(Hz) = 4,433,618.75 Hz */
SD_STD_PAL_BDGHI, 705268427, V4L2_STD_PAL,
},
};
static int adv7393_setstd(struct v4l2_subdev *sd, v4l2_std_id std)
{
struct adv7393_state *state = to_state(sd);
const struct adv7393_std_info *std_info;
int num_std;
u8 reg;
u32 val;
int err = 0;
int i;
num_std = ARRAY_SIZE(stdinfo);
for (i = 0; i < num_std; i++) {
if (stdinfo[i].stdid & std)
break;
}
if (i == num_std) {
v4l2_dbg(1, debug, sd,
"Invalid std or std is not supported: %llx\n",
(unsigned long long)std);
return -EINVAL;
}
std_info = &stdinfo[i];
/* Set the standard */
val = state->reg80 & ~SD_STD_MASK;
val |= std_info->standard_val3;
err = adv7393_write(sd, ADV7393_SD_MODE_REG1, val);
if (err < 0)
goto setstd_exit;
state->reg80 = val;
/* Configure the input mode register */
val = state->reg01 & ~INPUT_MODE_MASK;
val |= SD_INPUT_MODE;
err = adv7393_write(sd, ADV7393_MODE_SELECT_REG, val);
if (err < 0)
goto setstd_exit;
state->reg01 = val;
/* Program the sub carrier frequency registers */
val = std_info->fsc_val;
for (reg = ADV7393_FSC_REG0; reg <= ADV7393_FSC_REG3; reg++) {
err = adv7393_write(sd, reg, val);
if (err < 0)
goto setstd_exit;
val >>= 8;
}
val = state->reg82;
/* Pedestal settings */
if (std & (V4L2_STD_NTSC | V4L2_STD_NTSC_443))
val |= SD_PEDESTAL_EN;
else
val &= SD_PEDESTAL_DI;
err = adv7393_write(sd, ADV7393_SD_MODE_REG2, val);
if (err < 0)
goto setstd_exit;
state->reg82 = val;
setstd_exit:
if (err != 0)
v4l2_err(sd, "Error setting std, write failed\n");
return err;
}
static int adv7393_setoutput(struct v4l2_subdev *sd, u32 output_type)
{
struct adv7393_state *state = to_state(sd);
u8 val;
int err = 0;
if (output_type > ADV7393_SVIDEO_ID) {
v4l2_dbg(1, debug, sd,
"Invalid output type or output type not supported:%d\n",
output_type);
return -EINVAL;
}
/* Enable Appropriate DAC */
val = state->reg00 & 0x03;
if (output_type == ADV7393_COMPOSITE_ID)
val |= ADV7393_COMPOSITE_POWER_VALUE;
else if (output_type == ADV7393_COMPONENT_ID)
val |= ADV7393_COMPONENT_POWER_VALUE;
else
val |= ADV7393_SVIDEO_POWER_VALUE;
err = adv7393_write(sd, ADV7393_POWER_MODE_REG, val);
if (err < 0)
goto setoutput_exit;
state->reg00 = val;
/* Enable YUV output */
val = state->reg02 | YUV_OUTPUT_SELECT;
err = adv7393_write(sd, ADV7393_MODE_REG0, val);
if (err < 0)
goto setoutput_exit;
state->reg02 = val;
/* configure SD DAC Output 1 bit */
val = state->reg82;
if (output_type == ADV7393_COMPONENT_ID)
val &= SD_DAC_OUT1_DI;
else
val |= SD_DAC_OUT1_EN;
err = adv7393_write(sd, ADV7393_SD_MODE_REG2, val);
if (err < 0)
goto setoutput_exit;
state->reg82 = val;
/* configure ED/HD Color DAC Swap bit to zero */
val = state->reg35 & HD_DAC_SWAP_DI;
err = adv7393_write(sd, ADV7393_HD_MODE_REG6, val);
if (err < 0)
goto setoutput_exit;
state->reg35 = val;
setoutput_exit:
if (err != 0)
v4l2_err(sd, "Error setting output, write failed\n");
return err;
}
static int adv7393_log_status(struct v4l2_subdev *sd)
{
struct adv7393_state *state = to_state(sd);
v4l2_info(sd, "Standard: %llx\n", (unsigned long long)state->std);
v4l2_info(sd, "Output: %s\n", (state->output == 0) ? "Composite" :
((state->output == 1) ? "Component" : "S-Video"));
return 0;
}
static int adv7393_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = to_sd(ctrl);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
return adv7393_write(sd, ADV7393_SD_BRIGHTNESS_WSS,
ctrl->val & SD_BRIGHTNESS_VALUE_MASK);
case V4L2_CID_HUE:
return adv7393_write(sd, ADV7393_SD_HUE_ADJUST,
ctrl->val - ADV7393_HUE_MIN);
case V4L2_CID_GAIN:
return adv7393_write(sd, ADV7393_DAC123_OUTPUT_LEVEL,
ctrl->val);
}
return -EINVAL;
}
static int adv7393_g_chip_ident(struct v4l2_subdev *sd,
struct v4l2_dbg_chip_ident *chip)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7393, 0);
}
static const struct v4l2_ctrl_ops adv7393_ctrl_ops = {
.s_ctrl = adv7393_s_ctrl,
};
static const struct v4l2_subdev_core_ops adv7393_core_ops = {
.log_status = adv7393_log_status,
.g_chip_ident = adv7393_g_chip_ident,
.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 int adv7393_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
{
struct adv7393_state *state = to_state(sd);
int err = 0;
if (state->std == std)
return 0;
err = adv7393_setstd(sd, std);
if (!err)
state->std = std;
return err;
}
static int adv7393_s_routing(struct v4l2_subdev *sd,
u32 input, u32 output, u32 config)
{
struct adv7393_state *state = to_state(sd);
int err = 0;
if (state->output == output)
return 0;
err = adv7393_setoutput(sd, output);
if (!err)
state->output = output;
return err;
}
static const struct v4l2_subdev_video_ops adv7393_video_ops = {
.s_std_output = adv7393_s_std_output,
.s_routing = adv7393_s_routing,
};
static const struct v4l2_subdev_ops adv7393_ops = {
.core = &adv7393_core_ops,
.video = &adv7393_video_ops,
};
static int adv7393_initialize(struct v4l2_subdev *sd)
{
struct adv7393_state *state = to_state(sd);
int err = 0;
int i;
for (i = 0; i < ARRAY_SIZE(adv7393_init_reg_val); i += 2) {
err = adv7393_write(sd, adv7393_init_reg_val[i],
adv7393_init_reg_val[i+1]);
if (err) {
v4l2_err(sd, "Error initializing\n");
return err;
}
}
/* Configure for default video standard */
err = adv7393_setoutput(sd, state->output);
if (err < 0) {
v4l2_err(sd, "Error setting output during init\n");
return -EINVAL;
}
err = adv7393_setstd(sd, state->std);
if (err < 0) {
v4l2_err(sd, "Error setting std during init\n");
return -EINVAL;
}
return err;
}
static int adv7393_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct adv7393_state *state;
int err;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
state = kzalloc(sizeof(struct adv7393_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
state->reg00 = ADV7393_POWER_MODE_REG_DEFAULT;
state->reg01 = 0x00;
state->reg02 = 0x20;
state->reg35 = ADV7393_HD_MODE_REG6_DEFAULT;
state->reg80 = ADV7393_SD_MODE_REG1_DEFAULT;
state->reg82 = ADV7393_SD_MODE_REG2_DEFAULT;
state->output = ADV7393_COMPOSITE_ID;
state->std = V4L2_STD_NTSC;
v4l2_i2c_subdev_init(&state->sd, client, &adv7393_ops);
v4l2_ctrl_handler_init(&state->hdl, 3);
v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops,
V4L2_CID_BRIGHTNESS, ADV7393_BRIGHTNESS_MIN,
ADV7393_BRIGHTNESS_MAX, 1,
ADV7393_BRIGHTNESS_DEF);
v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops,
V4L2_CID_HUE, ADV7393_HUE_MIN,
ADV7393_HUE_MAX, 1,
ADV7393_HUE_DEF);
v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops,
V4L2_CID_GAIN, ADV7393_GAIN_MIN,
ADV7393_GAIN_MAX, 1,
ADV7393_GAIN_DEF);
state->sd.ctrl_handler = &state->hdl;
if (state->hdl.error) {
int err = state->hdl.error;
v4l2_ctrl_handler_free(&state->hdl);
kfree(state);
return err;
}
v4l2_ctrl_handler_setup(&state->hdl);
err = adv7393_initialize(&state->sd);
if (err) {
v4l2_ctrl_handler_free(&state->hdl);
kfree(state);
}
return err;
}
static int adv7393_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct adv7393_state *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 adv7393_id[] = {
{"adv7393", 0},
{},
};
MODULE_DEVICE_TABLE(i2c, adv7393_id);
static struct i2c_driver adv7393_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "adv7393",
},
.probe = adv7393_probe,
.remove = adv7393_remove,
.id_table = adv7393_id,
};
module_i2c_driver(adv7393_driver);

View file

@ -0,0 +1,188 @@
/*
* ADV7393 encoder related structure and register definitions
*
* Copyright (C) 2010-2012 ADVANSEE - http://www.advansee.com/
* Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
*
* Based on ADV7343 driver,
*
* Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed .as is. WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef ADV7393_REGS_H
#define ADV7393_REGS_H
struct adv7393_std_info {
u32 standard_val3;
u32 fsc_val;
v4l2_std_id stdid;
};
/* Register offset macros */
#define ADV7393_POWER_MODE_REG (0x00)
#define ADV7393_MODE_SELECT_REG (0x01)
#define ADV7393_MODE_REG0 (0x02)
#define ADV7393_DAC123_OUTPUT_LEVEL (0x0B)
#define ADV7393_SOFT_RESET (0x17)
#define ADV7393_HD_MODE_REG1 (0x30)
#define ADV7393_HD_MODE_REG2 (0x31)
#define ADV7393_HD_MODE_REG3 (0x32)
#define ADV7393_HD_MODE_REG4 (0x33)
#define ADV7393_HD_MODE_REG5 (0x34)
#define ADV7393_HD_MODE_REG6 (0x35)
#define ADV7393_HD_MODE_REG7 (0x39)
#define ADV7393_SD_MODE_REG1 (0x80)
#define ADV7393_SD_MODE_REG2 (0x82)
#define ADV7393_SD_MODE_REG3 (0x83)
#define ADV7393_SD_MODE_REG4 (0x84)
#define ADV7393_SD_MODE_REG5 (0x86)
#define ADV7393_SD_MODE_REG6 (0x87)
#define ADV7393_SD_MODE_REG7 (0x88)
#define ADV7393_SD_MODE_REG8 (0x89)
#define ADV7393_SD_TIMING_REG0 (0x8A)
#define ADV7393_FSC_REG0 (0x8C)
#define ADV7393_FSC_REG1 (0x8D)
#define ADV7393_FSC_REG2 (0x8E)
#define ADV7393_FSC_REG3 (0x8F)
#define ADV7393_SD_CGMS_WSS0 (0x99)
#define ADV7393_SD_HUE_ADJUST (0xA0)
#define ADV7393_SD_BRIGHTNESS_WSS (0xA1)
/* Default values for the registers */
#define ADV7393_POWER_MODE_REG_DEFAULT (0x10)
#define ADV7393_HD_MODE_REG1_DEFAULT (0x3C) /* Changed Default
720p EAV/SAV code*/
#define ADV7393_HD_MODE_REG2_DEFAULT (0x01) /* Changed Pixel data
valid */
#define ADV7393_HD_MODE_REG3_DEFAULT (0x00) /* Color delay 0 clks */
#define ADV7393_HD_MODE_REG4_DEFAULT (0xEC) /* Changed */
#define ADV7393_HD_MODE_REG5_DEFAULT (0x08)
#define ADV7393_HD_MODE_REG6_DEFAULT (0x00)
#define ADV7393_HD_MODE_REG7_DEFAULT (0x00)
#define ADV7393_SOFT_RESET_DEFAULT (0x02)
#define ADV7393_COMPOSITE_POWER_VALUE (0x10)
#define ADV7393_COMPONENT_POWER_VALUE (0x1C)
#define ADV7393_SVIDEO_POWER_VALUE (0x0C)
#define ADV7393_SD_HUE_ADJUST_DEFAULT (0x80)
#define ADV7393_SD_BRIGHTNESS_WSS_DEFAULT (0x00)
#define ADV7393_SD_CGMS_WSS0_DEFAULT (0x10)
#define ADV7393_SD_MODE_REG1_DEFAULT (0x10)
#define ADV7393_SD_MODE_REG2_DEFAULT (0xC9)
#define ADV7393_SD_MODE_REG3_DEFAULT (0x00)
#define ADV7393_SD_MODE_REG4_DEFAULT (0x00)
#define ADV7393_SD_MODE_REG5_DEFAULT (0x02)
#define ADV7393_SD_MODE_REG6_DEFAULT (0x8C)
#define ADV7393_SD_MODE_REG7_DEFAULT (0x14)
#define ADV7393_SD_MODE_REG8_DEFAULT (0x00)
#define ADV7393_SD_TIMING_REG0_DEFAULT (0x0C)
/* Bit masks for Mode Select Register */
#define INPUT_MODE_MASK (0x70)
#define SD_INPUT_MODE (0x00)
#define HD_720P_INPUT_MODE (0x10)
#define HD_1080I_INPUT_MODE (0x10)
/* Bit masks for Mode Register 0 */
#define TEST_PATTERN_BLACK_BAR_EN (0x04)
#define YUV_OUTPUT_SELECT (0x20)
#define RGB_OUTPUT_SELECT (0xDF)
/* Bit masks for SD brightness/WSS */
#define SD_BRIGHTNESS_VALUE_MASK (0x7F)
#define SD_BLANK_WSS_DATA_MASK (0x80)
/* Bit masks for soft reset register */
#define SOFT_RESET (0x02)
/* Bit masks for HD Mode Register 1 */
#define OUTPUT_STD_MASK (0x03)
#define OUTPUT_STD_SHIFT (0)
#define OUTPUT_STD_EIA0_2 (0x00)
#define OUTPUT_STD_EIA0_1 (0x01)
#define OUTPUT_STD_FULL (0x02)
#define EMBEDDED_SYNC (0x04)
#define EXTERNAL_SYNC (0xFB)
#define STD_MODE_MASK (0x1F)
#define STD_MODE_SHIFT (3)
#define STD_MODE_720P (0x05)
#define STD_MODE_720P_25 (0x08)
#define STD_MODE_720P_30 (0x07)
#define STD_MODE_720P_50 (0x06)
#define STD_MODE_1080I (0x0D)
#define STD_MODE_1080I_25 (0x0E)
#define STD_MODE_1080P_24 (0x11)
#define STD_MODE_1080P_25 (0x10)
#define STD_MODE_1080P_30 (0x0F)
#define STD_MODE_525P (0x00)
#define STD_MODE_625P (0x03)
/* Bit masks for SD Mode Register 1 */
#define SD_STD_MASK (0x03)
#define SD_STD_NTSC (0x00)
#define SD_STD_PAL_BDGHI (0x01)
#define SD_STD_PAL_M (0x02)
#define SD_STD_PAL_N (0x03)
#define SD_LUMA_FLTR_MASK (0x07)
#define SD_LUMA_FLTR_SHIFT (2)
#define SD_CHROMA_FLTR_MASK (0x07)
#define SD_CHROMA_FLTR_SHIFT (5)
/* Bit masks for SD Mode Register 2 */
#define SD_PRPB_SSAF_EN (0x01)
#define SD_PRPB_SSAF_DI (0xFE)
#define SD_DAC_OUT1_EN (0x02)
#define SD_DAC_OUT1_DI (0xFD)
#define SD_PEDESTAL_EN (0x08)
#define SD_PEDESTAL_DI (0xF7)
#define SD_SQUARE_PIXEL_EN (0x10)
#define SD_SQUARE_PIXEL_DI (0xEF)
#define SD_PIXEL_DATA_VALID (0x40)
#define SD_ACTIVE_EDGE_EN (0x80)
#define SD_ACTIVE_EDGE_DI (0x7F)
/* Bit masks for HD Mode Register 6 */
#define HD_PRPB_SYNC_EN (0x04)
#define HD_PRPB_SYNC_DI (0xFB)
#define HD_DAC_SWAP_EN (0x08)
#define HD_DAC_SWAP_DI (0xF7)
#define HD_GAMMA_CURVE_A (0xEF)
#define HD_GAMMA_CURVE_B (0x10)
#define HD_GAMMA_EN (0x20)
#define HD_GAMMA_DI (0xDF)
#define HD_ADPT_FLTR_MODEA (0xBF)
#define HD_ADPT_FLTR_MODEB (0x40)
#define HD_ADPT_FLTR_EN (0x80)
#define HD_ADPT_FLTR_DI (0x7F)
#define ADV7393_BRIGHTNESS_MAX (63)
#define ADV7393_BRIGHTNESS_MIN (-64)
#define ADV7393_BRIGHTNESS_DEF (0)
#define ADV7393_HUE_MAX (127)
#define ADV7393_HUE_MIN (-128)
#define ADV7393_HUE_DEF (0)
#define ADV7393_GAIN_MAX (64)
#define ADV7393_GAIN_MIN (-64)
#define ADV7393_GAIN_DEF (0)
#endif

359
drivers/media/i2c/ak881x.c Normal file
View file

@ -0,0 +1,359 @@
/*
* Driver for AK8813 / AK8814 TV-ecoders from Asahi Kasei Microsystems Co., Ltd. (AKM)
*
* Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <linux/module.h>
#include <media/ak881x.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
#define AK881X_INTERFACE_MODE 0
#define AK881X_VIDEO_PROCESS1 1
#define AK881X_VIDEO_PROCESS2 2
#define AK881X_VIDEO_PROCESS3 3
#define AK881X_DAC_MODE 5
#define AK881X_STATUS 0x24
#define AK881X_DEVICE_ID 0x25
#define AK881X_DEVICE_REVISION 0x26
struct ak881x {
struct v4l2_subdev subdev;
struct ak881x_pdata *pdata;
unsigned int lines;
int id; /* DEVICE_ID code V4L2_IDENT_AK881X code from v4l2-chip-ident.h */
char revision; /* DEVICE_REVISION content */
};
static int reg_read(struct i2c_client *client, const u8 reg)
{
return i2c_smbus_read_byte_data(client, reg);
}
static int reg_write(struct i2c_client *client, const u8 reg,
const u8 data)
{
return i2c_smbus_write_byte_data(client, reg, data);
}
static int reg_set(struct i2c_client *client, const u8 reg,
const u8 data, u8 mask)
{
int ret = reg_read(client, reg);
if (ret < 0)
return ret;
return reg_write(client, reg, (ret & ~mask) | (data & mask));
}
static struct ak881x *to_ak881x(const struct i2c_client *client)
{
return container_of(i2c_get_clientdata(client), struct ak881x, subdev);
}
static int ak881x_g_chip_ident(struct v4l2_subdev *sd,
struct v4l2_dbg_chip_ident *id)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ak881x *ak881x = to_ak881x(client);
if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
return -EINVAL;
if (id->match.addr != client->addr)
return -ENODEV;
id->ident = ak881x->id;
id->revision = ak881x->revision;
return 0;
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int ak881x_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x26)
return -EINVAL;
if (reg->match.addr != client->addr)
return -ENODEV;
reg->val = reg_read(client, reg->reg);
if (reg->val > 0xffff)
return -EIO;
return 0;
}
static int ak881x_s_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x26)
return -EINVAL;
if (reg->match.addr != client->addr)
return -ENODEV;
if (reg_write(client, reg->reg, reg->val) < 0)
return -EIO;
return 0;
}
#endif
static int ak881x_try_g_mbus_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ak881x *ak881x = to_ak881x(client);
v4l_bound_align_image(&mf->width, 0, 720, 2,
&mf->height, 0, ak881x->lines, 1, 0);
mf->field = V4L2_FIELD_INTERLACED;
mf->code = V4L2_MBUS_FMT_YUYV8_2X8;
mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
return 0;
}
static int ak881x_s_mbus_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
if (mf->field != V4L2_FIELD_INTERLACED ||
mf->code != V4L2_MBUS_FMT_YUYV8_2X8)
return -EINVAL;
return ak881x_try_g_mbus_fmt(sd, mf);
}
static int ak881x_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index,
enum v4l2_mbus_pixelcode *code)
{
if (index)
return -EINVAL;
*code = V4L2_MBUS_FMT_YUYV8_2X8;
return 0;
}
static int ak881x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ak881x *ak881x = to_ak881x(client);
a->bounds.left = 0;
a->bounds.top = 0;
a->bounds.width = 720;
a->bounds.height = ak881x->lines;
a->defrect = a->bounds;
a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
a->pixelaspect.numerator = 1;
a->pixelaspect.denominator = 1;
return 0;
}
static int ak881x_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ak881x *ak881x = to_ak881x(client);
u8 vp1;
if (std == V4L2_STD_NTSC_443) {
vp1 = 3;
ak881x->lines = 480;
} else if (std == V4L2_STD_PAL_M) {
vp1 = 5;
ak881x->lines = 480;
} else if (std == V4L2_STD_PAL_60) {
vp1 = 7;
ak881x->lines = 480;
} else if (std && !(std & ~V4L2_STD_PAL)) {
vp1 = 0xf;
ak881x->lines = 576;
} else if (std && !(std & ~V4L2_STD_NTSC)) {
vp1 = 0;
ak881x->lines = 480;
} else {
/* No SECAM or PAL_N/Nc supported */
return -EINVAL;
}
reg_set(client, AK881X_VIDEO_PROCESS1, vp1, 0xf);
return 0;
}
static int ak881x_s_stream(struct v4l2_subdev *sd, int enable)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ak881x *ak881x = to_ak881x(client);
if (enable) {
u8 dac;
/* For colour-bar testing set bit 6 of AK881X_VIDEO_PROCESS1 */
/* Default: composite output */
if (ak881x->pdata->flags & AK881X_COMPONENT)
dac = 3;
else
dac = 4;
/* Turn on the DAC(s) */
reg_write(client, AK881X_DAC_MODE, dac);
dev_dbg(&client->dev, "chip status 0x%x\n",
reg_read(client, AK881X_STATUS));
} else {
/* ...and clear bit 6 of AK881X_VIDEO_PROCESS1 here */
reg_write(client, AK881X_DAC_MODE, 0);
dev_dbg(&client->dev, "chip status 0x%x\n",
reg_read(client, AK881X_STATUS));
}
return 0;
}
static struct v4l2_subdev_core_ops ak881x_subdev_core_ops = {
.g_chip_ident = ak881x_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ak881x_g_register,
.s_register = ak881x_s_register,
#endif
};
static struct v4l2_subdev_video_ops ak881x_subdev_video_ops = {
.s_mbus_fmt = ak881x_s_mbus_fmt,
.g_mbus_fmt = ak881x_try_g_mbus_fmt,
.try_mbus_fmt = ak881x_try_g_mbus_fmt,
.cropcap = ak881x_cropcap,
.enum_mbus_fmt = ak881x_enum_mbus_fmt,
.s_std_output = ak881x_s_std_output,
.s_stream = ak881x_s_stream,
};
static struct v4l2_subdev_ops ak881x_subdev_ops = {
.core = &ak881x_subdev_core_ops,
.video = &ak881x_subdev_video_ops,
};
static int ak881x_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct ak881x *ak881x;
u8 ifmode, data;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_warn(&adapter->dev,
"I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
return -EIO;
}
ak881x = kzalloc(sizeof(struct ak881x), GFP_KERNEL);
if (!ak881x)
return -ENOMEM;
v4l2_i2c_subdev_init(&ak881x->subdev, client, &ak881x_subdev_ops);
data = reg_read(client, AK881X_DEVICE_ID);
switch (data) {
case 0x13:
ak881x->id = V4L2_IDENT_AK8813;
break;
case 0x14:
ak881x->id = V4L2_IDENT_AK8814;
break;
default:
dev_err(&client->dev,
"No ak881x chip detected, register read %x\n", data);
kfree(ak881x);
return -ENODEV;
}
ak881x->revision = reg_read(client, AK881X_DEVICE_REVISION);
ak881x->pdata = client->dev.platform_data;
if (ak881x->pdata) {
if (ak881x->pdata->flags & AK881X_FIELD)
ifmode = 4;
else
ifmode = 0;
switch (ak881x->pdata->flags & AK881X_IF_MODE_MASK) {
case AK881X_IF_MODE_BT656:
ifmode |= 1;
break;
case AK881X_IF_MODE_MASTER:
ifmode |= 2;
break;
case AK881X_IF_MODE_SLAVE:
default:
break;
}
dev_dbg(&client->dev, "IF mode %x\n", ifmode);
/*
* "Line Blanking No." seems to be the same as the number of
* "black" lines on, e.g., SuperH VOU, whose default value of 20
* "incidentally" matches ak881x' default
*/
reg_write(client, AK881X_INTERFACE_MODE, ifmode | (20 << 3));
}
/* Hardware default: NTSC-M */
ak881x->lines = 480;
dev_info(&client->dev, "Detected an ak881x chip ID %x, revision %x\n",
data, ak881x->revision);
return 0;
}
static int ak881x_remove(struct i2c_client *client)
{
struct ak881x *ak881x = to_ak881x(client);
v4l2_device_unregister_subdev(&ak881x->subdev);
kfree(ak881x);
return 0;
}
static const struct i2c_device_id ak881x_id[] = {
{ "ak8813", 0 },
{ "ak8814", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ak881x_id);
static struct i2c_driver ak881x_i2c_driver = {
.driver = {
.name = "ak881x",
},
.probe = ak881x_probe,
.remove = ak881x_remove,
.id_table = ak881x_id,
};
module_i2c_driver(ak881x_i2c_driver);
MODULE_DESCRIPTION("TV-output driver for ak8813/ak8814");
MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,173 @@
/*
* Aptina Sensor PLL Configuration
*
* Copyright (C) 2012 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <linux/device.h>
#include <linux/gcd.h>
#include <linux/kernel.h>
#include <linux/lcm.h>
#include <linux/module.h>
#include "aptina-pll.h"
int aptina_pll_calculate(struct device *dev,
const struct aptina_pll_limits *limits,
struct aptina_pll *pll)
{
unsigned int mf_min;
unsigned int mf_max;
unsigned int p1_min;
unsigned int p1_max;
unsigned int p1;
unsigned int div;
dev_dbg(dev, "PLL: ext clock %u pix clock %u\n",
pll->ext_clock, pll->pix_clock);
if (pll->ext_clock < limits->ext_clock_min ||
pll->ext_clock > limits->ext_clock_max) {
dev_err(dev, "pll: invalid external clock frequency.\n");
return -EINVAL;
}
if (pll->pix_clock == 0 || pll->pix_clock > limits->pix_clock_max) {
dev_err(dev, "pll: invalid pixel clock frequency.\n");
return -EINVAL;
}
/* Compute the multiplier M and combined N*P1 divisor. */
div = gcd(pll->pix_clock, pll->ext_clock);
pll->m = pll->pix_clock / div;
div = pll->ext_clock / div;
/* We now have the smallest M and N*P1 values that will result in the
* desired pixel clock frequency, but they might be out of the valid
* range. Compute the factor by which we should multiply them given the
* following constraints:
*
* - minimum/maximum multiplier
* - minimum/maximum multiplier output clock frequency assuming the
* minimum/maximum N value
* - minimum/maximum combined N*P1 divisor
*/
mf_min = DIV_ROUND_UP(limits->m_min, pll->m);
mf_min = max(mf_min, limits->out_clock_min /
(pll->ext_clock / limits->n_min * pll->m));
mf_min = max(mf_min, limits->n_min * limits->p1_min / div);
mf_max = limits->m_max / pll->m;
mf_max = min(mf_max, limits->out_clock_max /
(pll->ext_clock / limits->n_max * pll->m));
mf_max = min(mf_max, DIV_ROUND_UP(limits->n_max * limits->p1_max, div));
dev_dbg(dev, "pll: mf min %u max %u\n", mf_min, mf_max);
if (mf_min > mf_max) {
dev_err(dev, "pll: no valid combined N*P1 divisor.\n");
return -EINVAL;
}
/*
* We're looking for the highest acceptable P1 value for which a
* multiplier factor MF exists that fulfills the following conditions:
*
* 1. p1 is in the [p1_min, p1_max] range given by the limits and is
* even
* 2. mf is in the [mf_min, mf_max] range computed above
* 3. div * mf is a multiple of p1, in order to compute
* n = div * mf / p1
* m = pll->m * mf
* 4. the internal clock frequency, given by ext_clock / n, is in the
* [int_clock_min, int_clock_max] range given by the limits
* 5. the output clock frequency, given by ext_clock / n * m, is in the
* [out_clock_min, out_clock_max] range given by the limits
*
* The first naive approach is to iterate over all p1 values acceptable
* according to (1) and all mf values acceptable according to (2), and
* stop at the first combination that fulfills (3), (4) and (5). This
* has a O(n^2) complexity.
*
* Instead of iterating over all mf values in the [mf_min, mf_max] range
* we can compute the mf increment between two acceptable values
* according to (3) with
*
* mf_inc = p1 / gcd(div, p1) (6)
*
* and round the minimum up to the nearest multiple of mf_inc. This will
* restrict the number of mf values to be checked.
*
* Furthermore, conditions (4) and (5) only restrict the range of
* acceptable p1 and mf values by modifying the minimum and maximum
* limits. (5) can be expressed as
*
* ext_clock / (div * mf / p1) * m * mf >= out_clock_min
* ext_clock / (div * mf / p1) * m * mf <= out_clock_max
*
* or
*
* p1 >= out_clock_min * div / (ext_clock * m) (7)
* p1 <= out_clock_max * div / (ext_clock * m)
*
* Similarly, (4) can be expressed as
*
* mf >= ext_clock * p1 / (int_clock_max * div) (8)
* mf <= ext_clock * p1 / (int_clock_min * div)
*
* We can thus iterate over the restricted p1 range defined by the
* combination of (1) and (7), and then compute the restricted mf range
* defined by the combination of (2), (6) and (8). If the resulting mf
* range is not empty, any value in the mf range is acceptable. We thus
* select the mf lwoer bound and the corresponding p1 value.
*/
if (limits->p1_min == 0) {
dev_err(dev, "pll: P1 minimum value must be >0.\n");
return -EINVAL;
}
p1_min = max(limits->p1_min, DIV_ROUND_UP(limits->out_clock_min * div,
pll->ext_clock * pll->m));
p1_max = min(limits->p1_max, limits->out_clock_max * div /
(pll->ext_clock * pll->m));
for (p1 = p1_max & ~1; p1 >= p1_min; p1 -= 2) {
unsigned int mf_inc = p1 / gcd(div, p1);
unsigned int mf_high;
unsigned int mf_low;
mf_low = roundup(max(mf_min, DIV_ROUND_UP(pll->ext_clock * p1,
limits->int_clock_max * div)), mf_inc);
mf_high = min(mf_max, pll->ext_clock * p1 /
(limits->int_clock_min * div));
if (mf_low > mf_high)
continue;
pll->n = div * mf_low / p1;
pll->m *= mf_low;
pll->p1 = p1;
dev_dbg(dev, "PLL: N %u M %u P1 %u\n", pll->n, pll->m, pll->p1);
return 0;
}
dev_err(dev, "pll: no valid N and P1 divisors found.\n");
return -EINVAL;
}
EXPORT_SYMBOL_GPL(aptina_pll_calculate);
MODULE_DESCRIPTION("Aptina PLL Helpers");
MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,56 @@
/*
* Aptina Sensor PLL Configuration
*
* Copyright (C) 2012 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#ifndef __APTINA_PLL_H
#define __APTINA_PLL_H
struct aptina_pll {
unsigned int ext_clock;
unsigned int pix_clock;
unsigned int n;
unsigned int m;
unsigned int p1;
};
struct aptina_pll_limits {
unsigned int ext_clock_min;
unsigned int ext_clock_max;
unsigned int int_clock_min;
unsigned int int_clock_max;
unsigned int out_clock_min;
unsigned int out_clock_max;
unsigned int pix_clock_max;
unsigned int n_min;
unsigned int n_max;
unsigned int m_min;
unsigned int m_max;
unsigned int p1_min;
unsigned int p1_max;
};
struct device;
int aptina_pll_calculate(struct device *dev,
const struct aptina_pll_limits *limits,
struct aptina_pll *pll);
#endif /* __APTINA_PLL_H */

888
drivers/media/i2c/as3645a.c Normal file
View file

@ -0,0 +1,888 @@
/*
* drivers/media/i2c/as3645a.c - AS3645A and LM3555 flash controllers driver
*
* Copyright (C) 2008-2011 Nokia Corporation
* Copyright (c) 2011, Intel Corporation.
*
* Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* TODO:
* - Check hardware FSTROBE control when sensor driver add support for this
*
*/
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <media/as3645a.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#define AS_TIMER_MS_TO_CODE(t) (((t) - 100) / 50)
#define AS_TIMER_CODE_TO_MS(c) (50 * (c) + 100)
/* Register definitions */
/* Read-only Design info register: Reset state: xxxx 0001 */
#define AS_DESIGN_INFO_REG 0x00
#define AS_DESIGN_INFO_FACTORY(x) (((x) >> 4))
#define AS_DESIGN_INFO_MODEL(x) ((x) & 0x0f)
/* Read-only Version control register: Reset state: 0000 0000
* for first engineering samples
*/
#define AS_VERSION_CONTROL_REG 0x01
#define AS_VERSION_CONTROL_RFU(x) (((x) >> 4))
#define AS_VERSION_CONTROL_VERSION(x) ((x) & 0x0f)
/* Read / Write (Indicator and timer register): Reset state: 0000 1111 */
#define AS_INDICATOR_AND_TIMER_REG 0x02
#define AS_INDICATOR_AND_TIMER_TIMEOUT_SHIFT 0
#define AS_INDICATOR_AND_TIMER_VREF_SHIFT 4
#define AS_INDICATOR_AND_TIMER_INDICATOR_SHIFT 6
/* Read / Write (Current set register): Reset state: 0110 1001 */
#define AS_CURRENT_SET_REG 0x03
#define AS_CURRENT_ASSIST_LIGHT_SHIFT 0
#define AS_CURRENT_LED_DET_ON (1 << 3)
#define AS_CURRENT_FLASH_CURRENT_SHIFT 4
/* Read / Write (Control register): Reset state: 1011 0100 */
#define AS_CONTROL_REG 0x04
#define AS_CONTROL_MODE_SETTING_SHIFT 0
#define AS_CONTROL_STROBE_ON (1 << 2)
#define AS_CONTROL_OUT_ON (1 << 3)
#define AS_CONTROL_EXT_TORCH_ON (1 << 4)
#define AS_CONTROL_STROBE_TYPE_EDGE (0 << 5)
#define AS_CONTROL_STROBE_TYPE_LEVEL (1 << 5)
#define AS_CONTROL_COIL_PEAK_SHIFT 6
/* Read only (D3 is read / write) (Fault and info): Reset state: 0000 x000 */
#define AS_FAULT_INFO_REG 0x05
#define AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT (1 << 1)
#define AS_FAULT_INFO_INDICATOR_LED (1 << 2)
#define AS_FAULT_INFO_LED_AMOUNT (1 << 3)
#define AS_FAULT_INFO_TIMEOUT (1 << 4)
#define AS_FAULT_INFO_OVER_TEMPERATURE (1 << 5)
#define AS_FAULT_INFO_SHORT_CIRCUIT (1 << 6)
#define AS_FAULT_INFO_OVER_VOLTAGE (1 << 7)
/* Boost register */
#define AS_BOOST_REG 0x0d
#define AS_BOOST_CURRENT_DISABLE (0 << 0)
#define AS_BOOST_CURRENT_ENABLE (1 << 0)
/* Password register is used to unlock boost register writing */
#define AS_PASSWORD_REG 0x0f
#define AS_PASSWORD_UNLOCK_VALUE 0x55
enum as_mode {
AS_MODE_EXT_TORCH = 0 << AS_CONTROL_MODE_SETTING_SHIFT,
AS_MODE_INDICATOR = 1 << AS_CONTROL_MODE_SETTING_SHIFT,
AS_MODE_ASSIST = 2 << AS_CONTROL_MODE_SETTING_SHIFT,
AS_MODE_FLASH = 3 << AS_CONTROL_MODE_SETTING_SHIFT,
};
/*
* struct as3645a
*
* @subdev: V4L2 subdev
* @pdata: Flash platform data
* @power_lock: Protects power_count
* @power_count: Power reference count
* @led_mode: V4L2 flash LED mode
* @timeout: Flash timeout in microseconds
* @flash_current: Flash current (0=200mA ... 15=500mA). Maximum
* values are 400mA for two LEDs and 500mA for one LED.
* @assist_current: Torch/Assist light current (0=20mA, 1=40mA ... 7=160mA)
* @indicator_current: Indicator LED current (0=0mA, 1=2.5mA ... 4=10mA)
* @strobe_source: Flash strobe source (software or external)
*/
struct as3645a {
struct v4l2_subdev subdev;
const struct as3645a_platform_data *pdata;
struct mutex power_lock;
int power_count;
/* Controls */
struct v4l2_ctrl_handler ctrls;
enum v4l2_flash_led_mode led_mode;
unsigned int timeout;
u8 flash_current;
u8 assist_current;
u8 indicator_current;
enum v4l2_flash_strobe_source strobe_source;
};
#define to_as3645a(sd) container_of(sd, struct as3645a, subdev)
/* Return negative errno else zero on success */
static int as3645a_write(struct as3645a *flash, u8 addr, u8 val)
{
struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
int rval;
rval = i2c_smbus_write_byte_data(client, addr, val);
dev_dbg(&client->dev, "Write Addr:%02X Val:%02X %s\n", addr, val,
rval < 0 ? "fail" : "ok");
return rval;
}
/* Return negative errno else a data byte received from the device. */
static int as3645a_read(struct as3645a *flash, u8 addr)
{
struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
int rval;
rval = i2c_smbus_read_byte_data(client, addr);
dev_dbg(&client->dev, "Read Addr:%02X Val:%02X %s\n", addr, rval,
rval < 0 ? "fail" : "ok");
return rval;
}
/* -----------------------------------------------------------------------------
* Hardware configuration and trigger
*/
/*
* as3645a_set_config - Set flash configuration registers
* @flash: The flash
*
* Configure the hardware with flash, assist and indicator currents, as well as
* flash timeout.
*
* Return 0 on success, or a negative error code if an I2C communication error
* occurred.
*/
static int as3645a_set_config(struct as3645a *flash)
{
int ret;
u8 val;
val = (flash->flash_current << AS_CURRENT_FLASH_CURRENT_SHIFT)
| (flash->assist_current << AS_CURRENT_ASSIST_LIGHT_SHIFT)
| AS_CURRENT_LED_DET_ON;
ret = as3645a_write(flash, AS_CURRENT_SET_REG, val);
if (ret < 0)
return ret;
val = AS_TIMER_MS_TO_CODE(flash->timeout / 1000)
<< AS_INDICATOR_AND_TIMER_TIMEOUT_SHIFT;
val |= (flash->pdata->vref << AS_INDICATOR_AND_TIMER_VREF_SHIFT)
| ((flash->indicator_current ? flash->indicator_current - 1 : 0)
<< AS_INDICATOR_AND_TIMER_INDICATOR_SHIFT);
return as3645a_write(flash, AS_INDICATOR_AND_TIMER_REG, val);
}
/*
* as3645a_set_control - Set flash control register
* @flash: The flash
* @mode: Desired output mode
* @on: Desired output state
*
* Configure the hardware with output mode and state.
*
* Return 0 on success, or a negative error code if an I2C communication error
* occurred.
*/
static int
as3645a_set_control(struct as3645a *flash, enum as_mode mode, bool on)
{
u8 reg;
/* Configure output parameters and operation mode. */
reg = (flash->pdata->peak << AS_CONTROL_COIL_PEAK_SHIFT)
| (on ? AS_CONTROL_OUT_ON : 0)
| mode;
if (flash->led_mode == V4L2_FLASH_LED_MODE_FLASH &&
flash->strobe_source == V4L2_FLASH_STROBE_SOURCE_EXTERNAL) {
reg |= AS_CONTROL_STROBE_TYPE_LEVEL
| AS_CONTROL_STROBE_ON;
}
return as3645a_write(flash, AS_CONTROL_REG, reg);
}
/*
* as3645a_set_output - Configure output and operation mode
* @flash: Flash controller
* @strobe: Strobe the flash (only valid in flash mode)
*
* Turn the LEDs output on/off and set the operation mode based on the current
* parameters.
*
* The AS3645A can't control the indicator LED independently of the flash/torch
* LED. If the flash controller is in V4L2_FLASH_LED_MODE_NONE mode, set the
* chip to indicator mode. Otherwise set it to assist light (torch) or flash
* mode.
*
* In indicator and assist modes, turn the output on/off based on the indicator
* and torch currents. In software strobe flash mode, turn the output on/off
* based on the strobe parameter.
*/
static int as3645a_set_output(struct as3645a *flash, bool strobe)
{
enum as_mode mode;
bool on;
switch (flash->led_mode) {
case V4L2_FLASH_LED_MODE_NONE:
on = flash->indicator_current != 0;
mode = AS_MODE_INDICATOR;
break;
case V4L2_FLASH_LED_MODE_TORCH:
on = true;
mode = AS_MODE_ASSIST;
break;
case V4L2_FLASH_LED_MODE_FLASH:
on = strobe;
mode = AS_MODE_FLASH;
break;
default:
BUG();
}
/* Configure output parameters and operation mode. */
return as3645a_set_control(flash, mode, on);
}
/* -----------------------------------------------------------------------------
* V4L2 controls
*/
static int as3645a_is_active(struct as3645a *flash)
{
int ret;
ret = as3645a_read(flash, AS_CONTROL_REG);
return ret < 0 ? ret : !!(ret & AS_CONTROL_OUT_ON);
}
static int as3645a_read_fault(struct as3645a *flash)
{
struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
int rval;
/* NOTE: reading register clear fault status */
rval = as3645a_read(flash, AS_FAULT_INFO_REG);
if (rval < 0)
return rval;
if (rval & AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT)
dev_dbg(&client->dev, "Inductor Peak limit fault\n");
if (rval & AS_FAULT_INFO_INDICATOR_LED)
dev_dbg(&client->dev, "Indicator LED fault: "
"Short circuit or open loop\n");
dev_dbg(&client->dev, "%u connected LEDs\n",
rval & AS_FAULT_INFO_LED_AMOUNT ? 2 : 1);
if (rval & AS_FAULT_INFO_TIMEOUT)
dev_dbg(&client->dev, "Timeout fault\n");
if (rval & AS_FAULT_INFO_OVER_TEMPERATURE)
dev_dbg(&client->dev, "Over temperature fault\n");
if (rval & AS_FAULT_INFO_SHORT_CIRCUIT)
dev_dbg(&client->dev, "Short circuit fault\n");
if (rval & AS_FAULT_INFO_OVER_VOLTAGE)
dev_dbg(&client->dev, "Over voltage fault: "
"Indicates missing capacitor or open connection\n");
return rval;
}
static int as3645a_get_ctrl(struct v4l2_ctrl *ctrl)
{
struct as3645a *flash =
container_of(ctrl->handler, struct as3645a, ctrls);
struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
int value;
switch (ctrl->id) {
case V4L2_CID_FLASH_FAULT:
value = as3645a_read_fault(flash);
if (value < 0)
return value;
ctrl->cur.val = 0;
if (value & AS_FAULT_INFO_SHORT_CIRCUIT)
ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
if (value & AS_FAULT_INFO_OVER_TEMPERATURE)
ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE;
if (value & AS_FAULT_INFO_TIMEOUT)
ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT;
if (value & AS_FAULT_INFO_OVER_VOLTAGE)
ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE;
if (value & AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT)
ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_CURRENT;
if (value & AS_FAULT_INFO_INDICATOR_LED)
ctrl->cur.val |= V4L2_FLASH_FAULT_INDICATOR;
break;
case V4L2_CID_FLASH_STROBE_STATUS:
if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) {
ctrl->cur.val = 0;
break;
}
value = as3645a_is_active(flash);
if (value < 0)
return value;
ctrl->cur.val = value;
break;
}
dev_dbg(&client->dev, "G_CTRL %08x:%d\n", ctrl->id, ctrl->cur.val);
return 0;
}
static int as3645a_set_ctrl(struct v4l2_ctrl *ctrl)
{
struct as3645a *flash =
container_of(ctrl->handler, struct as3645a, ctrls);
struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
int ret;
dev_dbg(&client->dev, "S_CTRL %08x:%d\n", ctrl->id, ctrl->val);
/* If a control that doesn't apply to the current mode is modified,
* we store the value and return immediately. The setting will be
* applied when the LED mode is changed. Otherwise we apply the setting
* immediately.
*/
switch (ctrl->id) {
case V4L2_CID_FLASH_LED_MODE:
if (flash->indicator_current)
return -EBUSY;
ret = as3645a_set_config(flash);
if (ret < 0)
return ret;
flash->led_mode = ctrl->val;
return as3645a_set_output(flash, false);
case V4L2_CID_FLASH_STROBE_SOURCE:
flash->strobe_source = ctrl->val;
/* Applies to flash mode only. */
if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
break;
return as3645a_set_output(flash, false);
case V4L2_CID_FLASH_STROBE:
if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
return -EBUSY;
return as3645a_set_output(flash, true);
case V4L2_CID_FLASH_STROBE_STOP:
if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
return -EBUSY;
return as3645a_set_output(flash, false);
case V4L2_CID_FLASH_TIMEOUT:
flash->timeout = ctrl->val;
/* Applies to flash mode only. */
if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
break;
return as3645a_set_config(flash);
case V4L2_CID_FLASH_INTENSITY:
flash->flash_current = (ctrl->val - AS3645A_FLASH_INTENSITY_MIN)
/ AS3645A_FLASH_INTENSITY_STEP;
/* Applies to flash mode only. */
if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
break;
return as3645a_set_config(flash);
case V4L2_CID_FLASH_TORCH_INTENSITY:
flash->assist_current =
(ctrl->val - AS3645A_TORCH_INTENSITY_MIN)
/ AS3645A_TORCH_INTENSITY_STEP;
/* Applies to torch mode only. */
if (flash->led_mode != V4L2_FLASH_LED_MODE_TORCH)
break;
return as3645a_set_config(flash);
case V4L2_CID_FLASH_INDICATOR_INTENSITY:
if (flash->led_mode != V4L2_FLASH_LED_MODE_NONE)
return -EBUSY;
flash->indicator_current =
(ctrl->val - AS3645A_INDICATOR_INTENSITY_MIN)
/ AS3645A_INDICATOR_INTENSITY_STEP;
ret = as3645a_set_config(flash);
if (ret < 0)
return ret;
if ((ctrl->val == 0) == (ctrl->cur.val == 0))
break;
return as3645a_set_output(flash, false);
}
return 0;
}
static const struct v4l2_ctrl_ops as3645a_ctrl_ops = {
.g_volatile_ctrl = as3645a_get_ctrl,
.s_ctrl = as3645a_set_ctrl,
};
/* -----------------------------------------------------------------------------
* V4L2 subdev core operations
*/
/* Put device into know state. */
static int as3645a_setup(struct as3645a *flash)
{
struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
int ret;
/* clear errors */
ret = as3645a_read(flash, AS_FAULT_INFO_REG);
if (ret < 0)
return ret;
dev_dbg(&client->dev, "Fault info: %02x\n", ret);
ret = as3645a_set_config(flash);
if (ret < 0)
return ret;
ret = as3645a_set_output(flash, false);
if (ret < 0)
return ret;
/* read status */
ret = as3645a_read_fault(flash);
if (ret < 0)
return ret;
dev_dbg(&client->dev, "AS_INDICATOR_AND_TIMER_REG: %02x\n",
as3645a_read(flash, AS_INDICATOR_AND_TIMER_REG));
dev_dbg(&client->dev, "AS_CURRENT_SET_REG: %02x\n",
as3645a_read(flash, AS_CURRENT_SET_REG));
dev_dbg(&client->dev, "AS_CONTROL_REG: %02x\n",
as3645a_read(flash, AS_CONTROL_REG));
return ret & ~AS_FAULT_INFO_LED_AMOUNT ? -EIO : 0;
}
static int __as3645a_set_power(struct as3645a *flash, int on)
{
int ret;
if (!on)
as3645a_set_control(flash, AS_MODE_EXT_TORCH, false);
if (flash->pdata->set_power) {
ret = flash->pdata->set_power(&flash->subdev, on);
if (ret < 0)
return ret;
}
if (!on)
return 0;
ret = as3645a_setup(flash);
if (ret < 0) {
if (flash->pdata->set_power)
flash->pdata->set_power(&flash->subdev, 0);
}
return ret;
}
static int as3645a_set_power(struct v4l2_subdev *sd, int on)
{
struct as3645a *flash = to_as3645a(sd);
int ret = 0;
mutex_lock(&flash->power_lock);
if (flash->power_count == !on) {
ret = __as3645a_set_power(flash, !!on);
if (ret < 0)
goto done;
}
flash->power_count += on ? 1 : -1;
WARN_ON(flash->power_count < 0);
done:
mutex_unlock(&flash->power_lock);
return ret;
}
static int as3645a_registered(struct v4l2_subdev *sd)
{
struct as3645a *flash = to_as3645a(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
int rval, man, model, rfu, version;
const char *vendor;
/* Power up the flash driver and read manufacturer ID, model ID, RFU
* and version.
*/
rval = as3645a_set_power(&flash->subdev, 1);
if (rval < 0)
return rval;
rval = as3645a_read(flash, AS_DESIGN_INFO_REG);
if (rval < 0)
goto power_off;
man = AS_DESIGN_INFO_FACTORY(rval);
model = AS_DESIGN_INFO_MODEL(rval);
rval = as3645a_read(flash, AS_VERSION_CONTROL_REG);
if (rval < 0)
goto power_off;
rfu = AS_VERSION_CONTROL_RFU(rval);
version = AS_VERSION_CONTROL_VERSION(rval);
/* Verify the chip model and version. */
if (model != 0x01 || rfu != 0x00) {
dev_err(&client->dev, "AS3645A not detected "
"(model %d rfu %d)\n", model, rfu);
rval = -ENODEV;
goto power_off;
}
switch (man) {
case 1:
vendor = "AMS, Austria Micro Systems";
break;
case 2:
vendor = "ADI, Analog Devices Inc.";
break;
case 3:
vendor = "NSC, National Semiconductor";
break;
case 4:
vendor = "NXP";
break;
case 5:
vendor = "TI, Texas Instrument";
break;
default:
vendor = "Unknown";
}
dev_info(&client->dev, "Chip vendor: %s (%d) Version: %d\n", vendor,
man, version);
rval = as3645a_write(flash, AS_PASSWORD_REG, AS_PASSWORD_UNLOCK_VALUE);
if (rval < 0)
goto power_off;
rval = as3645a_write(flash, AS_BOOST_REG, AS_BOOST_CURRENT_DISABLE);
if (rval < 0)
goto power_off;
/* Setup default values. This makes sure that the chip is in a known
* state, in case the power rail can't be controlled.
*/
rval = as3645a_setup(flash);
power_off:
as3645a_set_power(&flash->subdev, 0);
return rval;
}
static int as3645a_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
return as3645a_set_power(sd, 1);
}
static int as3645a_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
return as3645a_set_power(sd, 0);
}
static const struct v4l2_subdev_core_ops as3645a_core_ops = {
.s_power = as3645a_set_power,
};
static const struct v4l2_subdev_ops as3645a_ops = {
.core = &as3645a_core_ops,
};
static const struct v4l2_subdev_internal_ops as3645a_internal_ops = {
.registered = as3645a_registered,
.open = as3645a_open,
.close = as3645a_close,
};
/* -----------------------------------------------------------------------------
* I2C driver
*/
#ifdef CONFIG_PM
static int as3645a_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct v4l2_subdev *subdev = i2c_get_clientdata(client);
struct as3645a *flash = to_as3645a(subdev);
int rval;
if (flash->power_count == 0)
return 0;
rval = __as3645a_set_power(flash, 0);
dev_dbg(&client->dev, "Suspend %s\n", rval < 0 ? "failed" : "ok");
return rval;
}
static int as3645a_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct v4l2_subdev *subdev = i2c_get_clientdata(client);
struct as3645a *flash = to_as3645a(subdev);
int rval;
if (flash->power_count == 0)
return 0;
rval = __as3645a_set_power(flash, 1);
dev_dbg(&client->dev, "Resume %s\n", rval < 0 ? "fail" : "ok");
return rval;
}
#else
#define as3645a_suspend NULL
#define as3645a_resume NULL
#endif /* CONFIG_PM */
/*
* as3645a_init_controls - Create controls
* @flash: The flash
*
* The number of LEDs reported in platform data is used to compute default
* limits. Parameters passed through platform data can override those limits.
*/
static int __devinit as3645a_init_controls(struct as3645a *flash)
{
const struct as3645a_platform_data *pdata = flash->pdata;
struct v4l2_ctrl *ctrl;
int maximum;
v4l2_ctrl_handler_init(&flash->ctrls, 10);
/* V4L2_CID_FLASH_LED_MODE */
v4l2_ctrl_new_std_menu(&flash->ctrls, &as3645a_ctrl_ops,
V4L2_CID_FLASH_LED_MODE, 2, ~7,
V4L2_FLASH_LED_MODE_NONE);
/* V4L2_CID_FLASH_STROBE_SOURCE */
v4l2_ctrl_new_std_menu(&flash->ctrls, &as3645a_ctrl_ops,
V4L2_CID_FLASH_STROBE_SOURCE,
pdata->ext_strobe ? 1 : 0,
pdata->ext_strobe ? ~3 : ~1,
V4L2_FLASH_STROBE_SOURCE_SOFTWARE);
flash->strobe_source = V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
/* V4L2_CID_FLASH_STROBE */
v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
V4L2_CID_FLASH_STROBE, 0, 0, 0, 0);
/* V4L2_CID_FLASH_STROBE_STOP */
v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0);
/* V4L2_CID_FLASH_STROBE_STATUS */
ctrl = v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
V4L2_CID_FLASH_STROBE_STATUS, 0, 1, 1, 1);
if (ctrl != NULL)
ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
/* V4L2_CID_FLASH_TIMEOUT */
maximum = pdata->timeout_max;
v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
V4L2_CID_FLASH_TIMEOUT, AS3645A_FLASH_TIMEOUT_MIN,
maximum, AS3645A_FLASH_TIMEOUT_STEP, maximum);
flash->timeout = maximum;
/* V4L2_CID_FLASH_INTENSITY */
maximum = pdata->flash_max_current;
v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
V4L2_CID_FLASH_INTENSITY, AS3645A_FLASH_INTENSITY_MIN,
maximum, AS3645A_FLASH_INTENSITY_STEP, maximum);
flash->flash_current = (maximum - AS3645A_FLASH_INTENSITY_MIN)
/ AS3645A_FLASH_INTENSITY_STEP;
/* V4L2_CID_FLASH_TORCH_INTENSITY */
maximum = pdata->torch_max_current;
v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
V4L2_CID_FLASH_TORCH_INTENSITY,
AS3645A_TORCH_INTENSITY_MIN, maximum,
AS3645A_TORCH_INTENSITY_STEP,
AS3645A_TORCH_INTENSITY_MIN);
flash->assist_current = 0;
/* V4L2_CID_FLASH_INDICATOR_INTENSITY */
v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
V4L2_CID_FLASH_INDICATOR_INTENSITY,
AS3645A_INDICATOR_INTENSITY_MIN,
AS3645A_INDICATOR_INTENSITY_MAX,
AS3645A_INDICATOR_INTENSITY_STEP,
AS3645A_INDICATOR_INTENSITY_MIN);
flash->indicator_current = 0;
/* V4L2_CID_FLASH_FAULT */
ctrl = v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
V4L2_CID_FLASH_FAULT, 0,
V4L2_FLASH_FAULT_OVER_VOLTAGE |
V4L2_FLASH_FAULT_TIMEOUT |
V4L2_FLASH_FAULT_OVER_TEMPERATURE |
V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0);
if (ctrl != NULL)
ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
flash->subdev.ctrl_handler = &flash->ctrls;
return flash->ctrls.error;
}
static int __devinit as3645a_probe(struct i2c_client *client,
const struct i2c_device_id *devid)
{
struct as3645a *flash;
int ret;
if (client->dev.platform_data == NULL)
return -ENODEV;
flash = kzalloc(sizeof(*flash), GFP_KERNEL);
if (flash == NULL)
return -ENOMEM;
flash->pdata = client->dev.platform_data;
v4l2_i2c_subdev_init(&flash->subdev, client, &as3645a_ops);
flash->subdev.internal_ops = &as3645a_internal_ops;
flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
ret = as3645a_init_controls(flash);
if (ret < 0)
goto done;
ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0);
if (ret < 0)
goto done;
flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
mutex_init(&flash->power_lock);
flash->led_mode = V4L2_FLASH_LED_MODE_NONE;
done:
if (ret < 0) {
v4l2_ctrl_handler_free(&flash->ctrls);
kfree(flash);
}
return ret;
}
static int __devexit as3645a_remove(struct i2c_client *client)
{
struct v4l2_subdev *subdev = i2c_get_clientdata(client);
struct as3645a *flash = to_as3645a(subdev);
v4l2_device_unregister_subdev(subdev);
v4l2_ctrl_handler_free(&flash->ctrls);
media_entity_cleanup(&flash->subdev.entity);
mutex_destroy(&flash->power_lock);
kfree(flash);
return 0;
}
static const struct i2c_device_id as3645a_id_table[] = {
{ AS3645A_NAME, 0 },
{ },
};
MODULE_DEVICE_TABLE(i2c, as3645a_id_table);
static const struct dev_pm_ops as3645a_pm_ops = {
.suspend = as3645a_suspend,
.resume = as3645a_resume,
};
static struct i2c_driver as3645a_i2c_driver = {
.driver = {
.name = AS3645A_NAME,
.pm = &as3645a_pm_ops,
},
.probe = as3645a_probe,
.remove = __devexit_p(as3645a_remove),
.id_table = as3645a_id_table,
};
module_i2c_driver(as3645a_i2c_driver);
MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
MODULE_DESCRIPTION("LED flash driver for AS3645A, LM3555 and their clones");
MODULE_LICENSE("GPL");

517
drivers/media/i2c/bt819.c Normal file
View file

@ -0,0 +1,517 @@
/*
* bt819 - BT819A VideoStream Decoder (Rockwell Part)
*
* Copyright (C) 1999 Mike Bernson <mike@mlb.org>
* Copyright (C) 1998 Dave Perks <dperks@ibm.net>
*
* Modifications for LML33/DC10plus unified driver
* Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
*
* Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
* - moved over to linux>=2.4.x i2c protocol (9/9/2002)
*
* This code was modify/ported from the saa7111 driver written
* by Dave Perks.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/ioctl.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <linux/slab.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
#include <media/bt819.h>
MODULE_DESCRIPTION("Brooktree-819 video decoder driver");
MODULE_AUTHOR("Mike Bernson & Dave Perks");
MODULE_LICENSE("GPL");
static int debug;
module_param(debug, int, 0);
MODULE_PARM_DESC(debug, "Debug level (0-1)");
/* ----------------------------------------------------------------------- */
struct bt819 {
struct v4l2_subdev sd;
struct v4l2_ctrl_handler hdl;
unsigned char reg[32];
v4l2_std_id norm;
int ident;
int input;
int enable;
};
static inline struct bt819 *to_bt819(struct v4l2_subdev *sd)
{
return container_of(sd, struct bt819, sd);
}
static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
{
return &container_of(ctrl->handler, struct bt819, hdl)->sd;
}
struct timing {
int hactive;
int hdelay;
int vactive;
int vdelay;
int hscale;
int vscale;
};
/* for values, see the bt819 datasheet */
static struct timing timing_data[] = {
{864 - 24, 20, 625 - 2, 1, 0x0504, 0x0000},
{858 - 24, 20, 525 - 2, 1, 0x00f8, 0x0000},
};
/* ----------------------------------------------------------------------- */
static inline int bt819_write(struct bt819 *decoder, u8 reg, u8 value)
{
struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd);
decoder->reg[reg] = value;
return i2c_smbus_write_byte_data(client, reg, value);
}
static inline int bt819_setbit(struct bt819 *decoder, u8 reg, u8 bit, u8 value)
{
return bt819_write(decoder, reg,
(decoder->reg[reg] & ~(1 << bit)) | (value ? (1 << bit) : 0));
}
static int bt819_write_block(struct bt819 *decoder, const u8 *data, unsigned int len)
{
struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd);
int ret = -1;
u8 reg;
/* the bt819 has an autoincrement function, use it if
* the adapter understands raw I2C */
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
/* do raw I2C, not smbus compatible */
u8 block_data[32];
int block_len;
while (len >= 2) {
block_len = 0;
block_data[block_len++] = reg = data[0];
do {
block_data[block_len++] =
decoder->reg[reg++] = data[1];
len -= 2;
data += 2;
} while (len >= 2 && data[0] == reg && block_len < 32);
ret = i2c_master_send(client, block_data, block_len);
if (ret < 0)
break;
}
} else {
/* do some slow I2C emulation kind of thing */
while (len >= 2) {
reg = *data++;
ret = bt819_write(decoder, reg, *data++);
if (ret < 0)
break;
len -= 2;
}
}
return ret;
}
static inline int bt819_read(struct bt819 *decoder, u8 reg)
{
struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd);
return i2c_smbus_read_byte_data(client, reg);
}
static int bt819_init(struct v4l2_subdev *sd)
{
static unsigned char init[] = {
/*0x1f, 0x00,*/ /* Reset */
0x01, 0x59, /* 0x01 input format */
0x02, 0x00, /* 0x02 temporal decimation */
0x03, 0x12, /* 0x03 Cropping msb */
0x04, 0x16, /* 0x04 Vertical Delay, lsb */
0x05, 0xe0, /* 0x05 Vertical Active lsb */
0x06, 0x80, /* 0x06 Horizontal Delay lsb */
0x07, 0xd0, /* 0x07 Horizontal Active lsb */
0x08, 0x00, /* 0x08 Horizontal Scaling msb */
0x09, 0xf8, /* 0x09 Horizontal Scaling lsb */
0x0a, 0x00, /* 0x0a Brightness control */
0x0b, 0x30, /* 0x0b Miscellaneous control */
0x0c, 0xd8, /* 0x0c Luma Gain lsb */
0x0d, 0xfe, /* 0x0d Chroma Gain (U) lsb */
0x0e, 0xb4, /* 0x0e Chroma Gain (V) msb */
0x0f, 0x00, /* 0x0f Hue control */
0x12, 0x04, /* 0x12 Output Format */
0x13, 0x20, /* 0x13 Vertial Scaling msb 0x00
chroma comb OFF, line drop scaling, interlace scaling
BUG? Why does turning the chroma comb on fuck up color?
Bug in the bt819 stepping on my board?
*/
0x14, 0x00, /* 0x14 Vertial Scaling lsb */
0x16, 0x07, /* 0x16 Video Timing Polarity
ACTIVE=active low
FIELD: high=odd,
vreset=active high,
hreset=active high */
0x18, 0x68, /* 0x18 AGC Delay */
0x19, 0x5d, /* 0x19 Burst Gate Delay */
0x1a, 0x80, /* 0x1a ADC Interface */
};
struct bt819 *decoder = to_bt819(sd);
struct timing *timing = &timing_data[(decoder->norm & V4L2_STD_525_60) ? 1 : 0];
init[0x03 * 2 - 1] =
(((timing->vdelay >> 8) & 0x03) << 6) |
(((timing->vactive >> 8) & 0x03) << 4) |
(((timing->hdelay >> 8) & 0x03) << 2) |
((timing->hactive >> 8) & 0x03);
init[0x04 * 2 - 1] = timing->vdelay & 0xff;
init[0x05 * 2 - 1] = timing->vactive & 0xff;
init[0x06 * 2 - 1] = timing->hdelay & 0xff;
init[0x07 * 2 - 1] = timing->hactive & 0xff;
init[0x08 * 2 - 1] = timing->hscale >> 8;
init[0x09 * 2 - 1] = timing->hscale & 0xff;
/* 0x15 in array is address 0x19 */
init[0x15 * 2 - 1] = (decoder->norm & V4L2_STD_625_50) ? 115 : 93; /* Chroma burst delay */
/* reset */
bt819_write(decoder, 0x1f, 0x00);
mdelay(1);
/* init */
return bt819_write_block(decoder, init, sizeof(init));
}
/* ----------------------------------------------------------------------- */
static int bt819_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd)
{
struct bt819 *decoder = to_bt819(sd);
int status = bt819_read(decoder, 0x00);
int res = V4L2_IN_ST_NO_SIGNAL;
v4l2_std_id std;
if ((status & 0x80))
res = 0;
if ((status & 0x10))
std = V4L2_STD_PAL;
else
std = V4L2_STD_NTSC;
if (pstd)
*pstd = std;
if (pstatus)
*pstatus = res;
v4l2_dbg(1, debug, sd, "get status %x\n", status);
return 0;
}
static int bt819_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
{
return bt819_status(sd, NULL, std);
}
static int bt819_g_input_status(struct v4l2_subdev *sd, u32 *status)
{
return bt819_status(sd, status, NULL);
}
static int bt819_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
{
struct bt819 *decoder = to_bt819(sd);
struct timing *timing = NULL;
v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std);
if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL)
v4l2_err(sd, "no notify found!\n");
if (std & V4L2_STD_NTSC) {
v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL);
bt819_setbit(decoder, 0x01, 0, 1);
bt819_setbit(decoder, 0x01, 1, 0);
bt819_setbit(decoder, 0x01, 5, 0);
bt819_write(decoder, 0x18, 0x68);
bt819_write(decoder, 0x19, 0x5d);
/* bt819_setbit(decoder, 0x1a, 5, 1); */
timing = &timing_data[1];
} else if (std & V4L2_STD_PAL) {
v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL);
bt819_setbit(decoder, 0x01, 0, 1);
bt819_setbit(decoder, 0x01, 1, 1);
bt819_setbit(decoder, 0x01, 5, 1);
bt819_write(decoder, 0x18, 0x7f);
bt819_write(decoder, 0x19, 0x72);
/* bt819_setbit(decoder, 0x1a, 5, 0); */
timing = &timing_data[0];
} else {
v4l2_dbg(1, debug, sd, "unsupported norm %llx\n",
(unsigned long long)std);
return -EINVAL;
}
bt819_write(decoder, 0x03,
(((timing->vdelay >> 8) & 0x03) << 6) |
(((timing->vactive >> 8) & 0x03) << 4) |
(((timing->hdelay >> 8) & 0x03) << 2) |
((timing->hactive >> 8) & 0x03));
bt819_write(decoder, 0x04, timing->vdelay & 0xff);
bt819_write(decoder, 0x05, timing->vactive & 0xff);
bt819_write(decoder, 0x06, timing->hdelay & 0xff);
bt819_write(decoder, 0x07, timing->hactive & 0xff);
bt819_write(decoder, 0x08, (timing->hscale >> 8) & 0xff);
bt819_write(decoder, 0x09, timing->hscale & 0xff);
decoder->norm = std;
v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, NULL);
return 0;
}
static int bt819_s_routing(struct v4l2_subdev *sd,
u32 input, u32 output, u32 config)
{
struct bt819 *decoder = to_bt819(sd);
v4l2_dbg(1, debug, sd, "set input %x\n", input);
if (input > 7)
return -EINVAL;
if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL)
v4l2_err(sd, "no notify found!\n");
if (decoder->input != input) {
v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL);
decoder->input = input;
/* select mode */
if (decoder->input == 0) {
bt819_setbit(decoder, 0x0b, 6, 0);
bt819_setbit(decoder, 0x1a, 1, 1);
} else {
bt819_setbit(decoder, 0x0b, 6, 1);
bt819_setbit(decoder, 0x1a, 1, 0);
}
v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, NULL);
}
return 0;
}
static int bt819_s_stream(struct v4l2_subdev *sd, int enable)
{
struct bt819 *decoder = to_bt819(sd);
v4l2_dbg(1, debug, sd, "enable output %x\n", enable);
if (decoder->enable != enable) {
decoder->enable = enable;
bt819_setbit(decoder, 0x16, 7, !enable);
}
return 0;
}
static int bt819_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = to_sd(ctrl);
struct bt819 *decoder = to_bt819(sd);
int temp;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
bt819_write(decoder, 0x0a, ctrl->val);
break;
case V4L2_CID_CONTRAST:
bt819_write(decoder, 0x0c, ctrl->val & 0xff);
bt819_setbit(decoder, 0x0b, 2, ((ctrl->val >> 8) & 0x01));
break;
case V4L2_CID_SATURATION:
bt819_write(decoder, 0x0d, (ctrl->val >> 7) & 0xff);
bt819_setbit(decoder, 0x0b, 1, ((ctrl->val >> 15) & 0x01));
/* Ratio between U gain and V gain must stay the same as
the ratio between the default U and V gain values. */
temp = (ctrl->val * 180) / 254;
bt819_write(decoder, 0x0e, (temp >> 7) & 0xff);
bt819_setbit(decoder, 0x0b, 0, (temp >> 15) & 0x01);
break;
case V4L2_CID_HUE:
bt819_write(decoder, 0x0f, ctrl->val);
break;
default:
return -EINVAL;
}
return 0;
}
static int bt819_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
{
struct bt819 *decoder = to_bt819(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
return v4l2_chip_ident_i2c_client(client, chip, decoder->ident, 0);
}
/* ----------------------------------------------------------------------- */
static const struct v4l2_ctrl_ops bt819_ctrl_ops = {
.s_ctrl = bt819_s_ctrl,
};
static const struct v4l2_subdev_core_ops bt819_core_ops = {
.g_chip_ident = bt819_g_chip_ident,
.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,
.s_std = bt819_s_std,
};
static const struct v4l2_subdev_video_ops bt819_video_ops = {
.s_routing = bt819_s_routing,
.s_stream = bt819_s_stream,
.querystd = bt819_querystd,
.g_input_status = bt819_g_input_status,
};
static const struct v4l2_subdev_ops bt819_ops = {
.core = &bt819_core_ops,
.video = &bt819_video_ops,
};
/* ----------------------------------------------------------------------- */
static int bt819_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int i, ver;
struct bt819 *decoder;
struct v4l2_subdev *sd;
const char *name;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
decoder = kzalloc(sizeof(struct bt819), GFP_KERNEL);
if (decoder == NULL)
return -ENOMEM;
sd = &decoder->sd;
v4l2_i2c_subdev_init(sd, client, &bt819_ops);
ver = bt819_read(decoder, 0x17);
switch (ver & 0xf0) {
case 0x70:
name = "bt819a";
decoder->ident = V4L2_IDENT_BT819A;
break;
case 0x60:
name = "bt817a";
decoder->ident = V4L2_IDENT_BT817A;
break;
case 0x20:
name = "bt815a";
decoder->ident = V4L2_IDENT_BT815A;
break;
default:
v4l2_dbg(1, debug, sd,
"unknown chip version 0x%02x\n", ver);
return -ENODEV;
}
v4l_info(client, "%s found @ 0x%x (%s)\n", name,
client->addr << 1, client->adapter->name);
decoder->norm = V4L2_STD_NTSC;
decoder->input = 0;
decoder->enable = 1;
i = bt819_init(sd);
if (i < 0)
v4l2_dbg(1, debug, sd, "init status %d\n", i);
v4l2_ctrl_handler_init(&decoder->hdl, 4);
v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops,
V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops,
V4L2_CID_CONTRAST, 0, 511, 1, 0xd8);
v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops,
V4L2_CID_SATURATION, 0, 511, 1, 0xfe);
v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops,
V4L2_CID_HUE, -128, 127, 1, 0);
sd->ctrl_handler = &decoder->hdl;
if (decoder->hdl.error) {
int err = decoder->hdl.error;
v4l2_ctrl_handler_free(&decoder->hdl);
kfree(decoder);
return err;
}
v4l2_ctrl_handler_setup(&decoder->hdl);
return 0;
}
static int bt819_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct bt819 *decoder = to_bt819(sd);
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(&decoder->hdl);
kfree(decoder);
return 0;
}
/* ----------------------------------------------------------------------- */
static const struct i2c_device_id bt819_id[] = {
{ "bt819a", 0 },
{ "bt817a", 0 },
{ "bt815a", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, bt819_id);
static struct i2c_driver bt819_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "bt819",
},
.probe = bt819_probe,
.remove = bt819_remove,
.id_table = bt819_id,
};
module_i2c_driver(bt819_driver);

273
drivers/media/i2c/bt856.c Normal file
View file

@ -0,0 +1,273 @@
/*
* bt856 - BT856A Digital Video Encoder (Rockwell Part)
*
* Copyright (C) 1999 Mike Bernson <mike@mlb.org>
* Copyright (C) 1998 Dave Perks <dperks@ibm.net>
*
* Modifications for LML33/DC10plus unified driver
* Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
*
* This code was modify/ported from the saa7111 driver written
* by Dave Perks.
*
* Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
* - moved over to linux>=2.4.x i2c protocol (9/9/2002)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <asm/uaccess.h>
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
MODULE_DESCRIPTION("Brooktree-856A video encoder driver");
MODULE_AUTHOR("Mike Bernson & Dave Perks");
MODULE_LICENSE("GPL");
static int debug;
module_param(debug, int, 0);
MODULE_PARM_DESC(debug, "Debug level (0-1)");
/* ----------------------------------------------------------------------- */
#define BT856_REG_OFFSET 0xDA
#define BT856_NR_REG 6
struct bt856 {
struct v4l2_subdev sd;
unsigned char reg[BT856_NR_REG];
v4l2_std_id norm;
};
static inline struct bt856 *to_bt856(struct v4l2_subdev *sd)
{
return container_of(sd, struct bt856, sd);
}
/* ----------------------------------------------------------------------- */
static inline int bt856_write(struct bt856 *encoder, u8 reg, u8 value)
{
struct i2c_client *client = v4l2_get_subdevdata(&encoder->sd);
encoder->reg[reg - BT856_REG_OFFSET] = value;
return i2c_smbus_write_byte_data(client, reg, value);
}
static inline int bt856_setbit(struct bt856 *encoder, u8 reg, u8 bit, u8 value)
{
return bt856_write(encoder, reg,
(encoder->reg[reg - BT856_REG_OFFSET] & ~(1 << bit)) |
(value ? (1 << bit) : 0));
}
static void bt856_dump(struct bt856 *encoder)
{
int i;
v4l2_info(&encoder->sd, "register dump:\n");
for (i = 0; i < BT856_NR_REG; i += 2)
printk(KERN_CONT " %02x", encoder->reg[i]);
printk(KERN_CONT "\n");
}
/* ----------------------------------------------------------------------- */
static int bt856_init(struct v4l2_subdev *sd, u32 arg)
{
struct bt856 *encoder = to_bt856(sd);
/* This is just for testing!!! */
v4l2_dbg(1, debug, sd, "init\n");
bt856_write(encoder, 0xdc, 0x18);
bt856_write(encoder, 0xda, 0);
bt856_write(encoder, 0xde, 0);
bt856_setbit(encoder, 0xdc, 3, 1);
/*bt856_setbit(encoder, 0xdc, 6, 0);*/
bt856_setbit(encoder, 0xdc, 4, 1);
if (encoder->norm & V4L2_STD_NTSC)
bt856_setbit(encoder, 0xdc, 2, 0);
else
bt856_setbit(encoder, 0xdc, 2, 1);
bt856_setbit(encoder, 0xdc, 1, 1);
bt856_setbit(encoder, 0xde, 4, 0);
bt856_setbit(encoder, 0xde, 3, 1);
if (debug != 0)
bt856_dump(encoder);
return 0;
}
static int bt856_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
{
struct bt856 *encoder = to_bt856(sd);
v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std);
if (std & V4L2_STD_NTSC) {
bt856_setbit(encoder, 0xdc, 2, 0);
} else if (std & V4L2_STD_PAL) {
bt856_setbit(encoder, 0xdc, 2, 1);
bt856_setbit(encoder, 0xda, 0, 0);
/*bt856_setbit(encoder, 0xda, 0, 1);*/
} else {
return -EINVAL;
}
encoder->norm = std;
if (debug != 0)
bt856_dump(encoder);
return 0;
}
static int bt856_s_routing(struct v4l2_subdev *sd,
u32 input, u32 output, u32 config)
{
struct bt856 *encoder = to_bt856(sd);
v4l2_dbg(1, debug, sd, "set input %d\n", input);
/* We only have video bus.
* input= 0: input is from bt819
* input= 1: input is from ZR36060 */
switch (input) {
case 0:
bt856_setbit(encoder, 0xde, 4, 0);
bt856_setbit(encoder, 0xde, 3, 1);
bt856_setbit(encoder, 0xdc, 3, 1);
bt856_setbit(encoder, 0xdc, 6, 0);
break;
case 1:
bt856_setbit(encoder, 0xde, 4, 0);
bt856_setbit(encoder, 0xde, 3, 1);
bt856_setbit(encoder, 0xdc, 3, 1);
bt856_setbit(encoder, 0xdc, 6, 1);
break;
case 2: /* Color bar */
bt856_setbit(encoder, 0xdc, 3, 0);
bt856_setbit(encoder, 0xde, 4, 1);
break;
default:
return -EINVAL;
}
if (debug != 0)
bt856_dump(encoder);
return 0;
}
static int bt856_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_BT856, 0);
}
/* ----------------------------------------------------------------------- */
static const struct v4l2_subdev_core_ops bt856_core_ops = {
.g_chip_ident = bt856_g_chip_ident,
.init = bt856_init,
};
static const struct v4l2_subdev_video_ops bt856_video_ops = {
.s_std_output = bt856_s_std_output,
.s_routing = bt856_s_routing,
};
static const struct v4l2_subdev_ops bt856_ops = {
.core = &bt856_core_ops,
.video = &bt856_video_ops,
};
/* ----------------------------------------------------------------------- */
static int bt856_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct bt856 *encoder;
struct v4l2_subdev *sd;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
encoder = kzalloc(sizeof(struct bt856), GFP_KERNEL);
if (encoder == NULL)
return -ENOMEM;
sd = &encoder->sd;
v4l2_i2c_subdev_init(sd, client, &bt856_ops);
encoder->norm = V4L2_STD_NTSC;
bt856_write(encoder, 0xdc, 0x18);
bt856_write(encoder, 0xda, 0);
bt856_write(encoder, 0xde, 0);
bt856_setbit(encoder, 0xdc, 3, 1);
/*bt856_setbit(encoder, 0xdc, 6, 0);*/
bt856_setbit(encoder, 0xdc, 4, 1);
if (encoder->norm & V4L2_STD_NTSC)
bt856_setbit(encoder, 0xdc, 2, 0);
else
bt856_setbit(encoder, 0xdc, 2, 1);
bt856_setbit(encoder, 0xdc, 1, 1);
bt856_setbit(encoder, 0xde, 4, 0);
bt856_setbit(encoder, 0xde, 3, 1);
if (debug != 0)
bt856_dump(encoder);
return 0;
}
static int bt856_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(sd);
kfree(to_bt856(sd));
return 0;
}
static const struct i2c_device_id bt856_id[] = {
{ "bt856", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, bt856_id);
static struct i2c_driver bt856_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "bt856",
},
.probe = bt856_probe,
.remove = bt856_remove,
.id_table = bt856_id,
};
module_i2c_driver(bt856_driver);

Some files were not shown because too many files have changed in this diff Show more