|
@@ -32,23 +32,24 @@
|
|
|
#include <linux/mutex.h>
|
|
|
#include <linux/delay.h>
|
|
|
|
|
|
+#define ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM 0x0
|
|
|
+#define ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM_PED 0x1
|
|
|
+#define ADV7180_STD_AD_PAL_N_NTSC_J_SECAM 0x2
|
|
|
+#define ADV7180_STD_AD_PAL_N_NTSC_M_SECAM 0x3
|
|
|
+#define ADV7180_STD_NTSC_J 0x4
|
|
|
+#define ADV7180_STD_NTSC_M 0x5
|
|
|
+#define ADV7180_STD_PAL60 0x6
|
|
|
+#define ADV7180_STD_NTSC_443 0x7
|
|
|
+#define ADV7180_STD_PAL_BG 0x8
|
|
|
+#define ADV7180_STD_PAL_N 0x9
|
|
|
+#define ADV7180_STD_PAL_M 0xa
|
|
|
+#define ADV7180_STD_PAL_M_PED 0xb
|
|
|
+#define ADV7180_STD_PAL_COMB_N 0xc
|
|
|
+#define ADV7180_STD_PAL_COMB_N_PED 0xd
|
|
|
+#define ADV7180_STD_PAL_SECAM 0xe
|
|
|
+#define ADV7180_STD_PAL_SECAM_PED 0xf
|
|
|
+
|
|
|
#define ADV7180_REG_INPUT_CONTROL 0x0000
|
|
|
-#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_REG_EXTENDED_OUTPUT_CONTROL 0x0004
|
|
@@ -121,6 +122,30 @@
|
|
|
#define ADV7180_REG_NTSC_V_BIT_END 0x00E6
|
|
|
#define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND 0x4F
|
|
|
|
|
|
+#define ADV7180_INPUT_CVBS_AIN1 0x00
|
|
|
+#define ADV7180_INPUT_CVBS_AIN2 0x01
|
|
|
+#define ADV7180_INPUT_CVBS_AIN3 0x02
|
|
|
+#define ADV7180_INPUT_CVBS_AIN4 0x03
|
|
|
+#define ADV7180_INPUT_CVBS_AIN5 0x04
|
|
|
+#define ADV7180_INPUT_CVBS_AIN6 0x05
|
|
|
+#define ADV7180_INPUT_SVIDEO_AIN1_AIN2 0x06
|
|
|
+#define ADV7180_INPUT_SVIDEO_AIN3_AIN4 0x07
|
|
|
+#define ADV7180_INPUT_SVIDEO_AIN5_AIN6 0x08
|
|
|
+#define ADV7180_INPUT_YPRPB_AIN1_AIN2_AIN3 0x09
|
|
|
+#define ADV7180_INPUT_YPRPB_AIN4_AIN5_AIN6 0x0a
|
|
|
+
|
|
|
+struct adv7180_state;
|
|
|
+
|
|
|
+#define ADV7180_FLAG_RESET_POWERED BIT(0)
|
|
|
+
|
|
|
+struct adv7180_chip_info {
|
|
|
+ unsigned int flags;
|
|
|
+ unsigned int valid_input_mask;
|
|
|
+ int (*set_std)(struct adv7180_state *st, unsigned int std);
|
|
|
+ int (*select_input)(struct adv7180_state *st, unsigned int input);
|
|
|
+ int (*init)(struct adv7180_state *state);
|
|
|
+};
|
|
|
+
|
|
|
struct adv7180_state {
|
|
|
struct v4l2_ctrl_handler ctrl_hdl;
|
|
|
struct v4l2_subdev sd;
|
|
@@ -134,6 +159,7 @@ struct adv7180_state {
|
|
|
|
|
|
struct i2c_client *client;
|
|
|
unsigned int register_page;
|
|
|
+ const struct adv7180_chip_info *chip_info;
|
|
|
};
|
|
|
#define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler, \
|
|
|
struct adv7180_state, \
|
|
@@ -165,6 +191,11 @@ static int adv7180_read(struct adv7180_state *state, unsigned int reg)
|
|
|
return i2c_smbus_read_byte_data(state->client, reg & 0xff);
|
|
|
}
|
|
|
|
|
|
+static int adv7180_set_video_standard(struct adv7180_state *state,
|
|
|
+ unsigned int std)
|
|
|
+{
|
|
|
+ return state->chip_info->set_std(state, std);
|
|
|
+}
|
|
|
|
|
|
static v4l2_std_id adv7180_std_to_v4l2(u8 status1)
|
|
|
{
|
|
@@ -197,22 +228,22 @@ static v4l2_std_id adv7180_std_to_v4l2(u8 status1)
|
|
|
static int v4l2_std_to_adv7180(v4l2_std_id std)
|
|
|
{
|
|
|
if (std == V4L2_STD_PAL_60)
|
|
|
- return ADV7180_INPUT_CONTROL_PAL60;
|
|
|
+ return ADV7180_STD_PAL60;
|
|
|
if (std == V4L2_STD_NTSC_443)
|
|
|
- return ADV7180_INPUT_CONTROL_NTSC_443;
|
|
|
+ return ADV7180_STD_NTSC_443;
|
|
|
if (std == V4L2_STD_PAL_N)
|
|
|
- return ADV7180_INPUT_CONTROL_PAL_N;
|
|
|
+ return ADV7180_STD_PAL_N;
|
|
|
if (std == V4L2_STD_PAL_M)
|
|
|
- return ADV7180_INPUT_CONTROL_PAL_M;
|
|
|
+ return ADV7180_STD_PAL_M;
|
|
|
if (std == V4L2_STD_PAL_Nc)
|
|
|
- return ADV7180_INPUT_CONTROL_PAL_COMB_N;
|
|
|
+ return ADV7180_STD_PAL_COMB_N;
|
|
|
|
|
|
if (std & V4L2_STD_PAL)
|
|
|
- return ADV7180_INPUT_CONTROL_PAL_BG;
|
|
|
+ return ADV7180_STD_PAL_BG;
|
|
|
if (std & V4L2_STD_NTSC)
|
|
|
- return ADV7180_INPUT_CONTROL_NTSC_M;
|
|
|
+ return ADV7180_STD_NTSC_M;
|
|
|
if (std & V4L2_STD_SECAM)
|
|
|
- return ADV7180_INPUT_CONTROL_PAL_SECAM;
|
|
|
+ return ADV7180_STD_PAL_SECAM;
|
|
|
|
|
|
return -EINVAL;
|
|
|
}
|
|
@@ -272,19 +303,15 @@ static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input,
|
|
|
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)
|
|
|
+ if (input > 31 || !(BIT(input) & state->chip_info->valid_input_mask)) {
|
|
|
+ ret = -EINVAL;
|
|
|
goto out;
|
|
|
+ }
|
|
|
|
|
|
- ret = adv7180_read(state, ADV7180_REG_INPUT_CONTROL);
|
|
|
- if (ret < 0)
|
|
|
- goto out;
|
|
|
+ ret = state->chip_info->select_input(state, input);
|
|
|
|
|
|
- ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK;
|
|
|
- ret = adv7180_write(state, ADV7180_REG_INPUT_CONTROL, ret | input);
|
|
|
- state->input = input;
|
|
|
+ if (ret == 0)
|
|
|
+ state->input = input;
|
|
|
out:
|
|
|
mutex_unlock(&state->mutex);
|
|
|
return ret;
|
|
@@ -307,9 +334,8 @@ static int adv7180_program_std(struct adv7180_state *state)
|
|
|
int ret;
|
|
|
|
|
|
if (state->autodetect) {
|
|
|
- ret = adv7180_write(state, ADV7180_REG_INPUT_CONTROL,
|
|
|
- ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM
|
|
|
- | state->input);
|
|
|
+ ret = adv7180_set_video_standard(state,
|
|
|
+ ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
@@ -319,8 +345,7 @@ static int adv7180_program_std(struct adv7180_state *state)
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
|
- ret = adv7180_write(state, ADV7180_REG_INPUT_CONTROL,
|
|
|
- ret | state->input);
|
|
|
+ ret = adv7180_set_video_standard(state, ret);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
}
|
|
@@ -521,6 +546,7 @@ static const struct v4l2_subdev_video_ops adv7180_video_ops = {
|
|
|
.g_mbus_config = adv7180_g_mbus_config,
|
|
|
};
|
|
|
|
|
|
+
|
|
|
static const struct v4l2_subdev_core_ops adv7180_core_ops = {
|
|
|
.s_power = adv7180_s_power,
|
|
|
};
|
|
@@ -554,33 +580,77 @@ static irqreturn_t adv7180_irq(int irq, void *devid)
|
|
|
return IRQ_HANDLED;
|
|
|
}
|
|
|
|
|
|
-static int init_device(struct adv7180_state *state)
|
|
|
+static int adv7180_init(struct adv7180_state *state)
|
|
|
{
|
|
|
int ret;
|
|
|
|
|
|
- mutex_lock(&state->mutex);
|
|
|
-
|
|
|
- adv7180_write(state, ADV7180_REG_PWR_MAN, ADV7180_PWR_MAN_RES);
|
|
|
- usleep_range(2000, 10000);
|
|
|
-
|
|
|
- ret = adv7180_program_std(state);
|
|
|
- if (ret)
|
|
|
- goto out_unlock;
|
|
|
-
|
|
|
/* ITU-R BT.656-4 compatible */
|
|
|
ret = adv7180_write(state, ADV7180_REG_EXTENDED_OUTPUT_CONTROL,
|
|
|
ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS);
|
|
|
if (ret < 0)
|
|
|
- goto out_unlock;
|
|
|
+ return ret;
|
|
|
|
|
|
/* Manually set V bit end position in NTSC mode */
|
|
|
- ret = adv7180_write(state, ADV7180_REG_NTSC_V_BIT_END,
|
|
|
+ return adv7180_write(state, ADV7180_REG_NTSC_V_BIT_END,
|
|
|
ADV7180_NTSC_V_BIT_END_MANUAL_NVEND);
|
|
|
+}
|
|
|
+
|
|
|
+static int adv7180_set_std(struct adv7180_state *state, unsigned int std)
|
|
|
+{
|
|
|
+ return adv7180_write(state, ADV7180_REG_INPUT_CONTROL,
|
|
|
+ (std << 4) | state->input);
|
|
|
+}
|
|
|
+
|
|
|
+static int adv7180_select_input(struct adv7180_state *state, unsigned int input)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = adv7180_read(state, ADV7180_REG_INPUT_CONTROL);
|
|
|
if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK;
|
|
|
+ ret |= input;
|
|
|
+ return adv7180_write(state, ADV7180_REG_INPUT_CONTROL, ret);
|
|
|
+}
|
|
|
+
|
|
|
+static const struct adv7180_chip_info adv7180_info = {
|
|
|
+ .flags = ADV7180_FLAG_RESET_POWERED,
|
|
|
+ /* We cannot discriminate between LQFP and 40-pin LFCSP, so accept
|
|
|
+ * all inputs and let the card driver take care of validation
|
|
|
+ */
|
|
|
+ .valid_input_mask = BIT(ADV7180_INPUT_CVBS_AIN1) |
|
|
|
+ BIT(ADV7180_INPUT_CVBS_AIN2) |
|
|
|
+ BIT(ADV7180_INPUT_CVBS_AIN3) |
|
|
|
+ BIT(ADV7180_INPUT_CVBS_AIN4) |
|
|
|
+ BIT(ADV7180_INPUT_CVBS_AIN5) |
|
|
|
+ BIT(ADV7180_INPUT_CVBS_AIN6) |
|
|
|
+ BIT(ADV7180_INPUT_SVIDEO_AIN1_AIN2) |
|
|
|
+ BIT(ADV7180_INPUT_SVIDEO_AIN3_AIN4) |
|
|
|
+ BIT(ADV7180_INPUT_SVIDEO_AIN5_AIN6) |
|
|
|
+ BIT(ADV7180_INPUT_YPRPB_AIN1_AIN2_AIN3) |
|
|
|
+ BIT(ADV7180_INPUT_YPRPB_AIN4_AIN5_AIN6),
|
|
|
+ .init = adv7180_init,
|
|
|
+ .set_std = adv7180_set_std,
|
|
|
+ .select_input = adv7180_select_input,
|
|
|
+};
|
|
|
+
|
|
|
+static int init_device(struct adv7180_state *state)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ mutex_lock(&state->mutex);
|
|
|
+
|
|
|
+ adv7180_write(state, ADV7180_REG_PWR_MAN, ADV7180_PWR_MAN_RES);
|
|
|
+ usleep_range(2000, 10000);
|
|
|
+
|
|
|
+ ret = state->chip_info->init(state);
|
|
|
+ if (ret)
|
|
|
goto out_unlock;
|
|
|
|
|
|
- /* read current norm */
|
|
|
- __adv7180_status(state, NULL, &state->curr_norm);
|
|
|
+ ret = adv7180_program_std(state);
|
|
|
+ if (ret)
|
|
|
+ goto out_unlock;
|
|
|
|
|
|
/* register for interrupts */
|
|
|
if (state->irq > 0) {
|
|
@@ -635,11 +705,15 @@ static int adv7180_probe(struct i2c_client *client,
|
|
|
return -ENOMEM;
|
|
|
|
|
|
state->client = client;
|
|
|
+ state->chip_info = (struct adv7180_chip_info *)id->driver_data;
|
|
|
|
|
|
state->irq = client->irq;
|
|
|
mutex_init(&state->mutex);
|
|
|
state->autodetect = true;
|
|
|
- state->powered = true;
|
|
|
+ if (state->chip_info->flags & ADV7180_FLAG_RESET_POWERED)
|
|
|
+ state->powered = true;
|
|
|
+ else
|
|
|
+ state->powered = false;
|
|
|
state->input = 0;
|
|
|
sd = &state->sd;
|
|
|
v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
|
|
@@ -702,9 +776,10 @@ static int adv7180_remove(struct i2c_client *client)
|
|
|
}
|
|
|
|
|
|
static const struct i2c_device_id adv7180_id[] = {
|
|
|
- {KBUILD_MODNAME, 0},
|
|
|
+ { "adv7180", (kernel_ulong_t)&adv7180_info },
|
|
|
{},
|
|
|
};
|
|
|
+MODULE_DEVICE_TABLE(i2c, adv7180_id);
|
|
|
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
|
static int adv7180_suspend(struct device *dev)
|
|
@@ -741,8 +816,6 @@ static SIMPLE_DEV_PM_OPS(adv7180_pm_ops, adv7180_suspend, adv7180_resume);
|
|
|
#define ADV7180_PM_OPS NULL
|
|
|
#endif
|
|
|
|
|
|
-MODULE_DEVICE_TABLE(i2c, adv7180_id);
|
|
|
-
|
|
|
static struct i2c_driver adv7180_driver = {
|
|
|
.driver = {
|
|
|
.owner = THIS_MODULE,
|