aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/i2c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/i2c')
-rw-r--r--drivers/media/i2c/Kconfig24
-rw-r--r--drivers/media/i2c/Makefile2
-rw-r--r--drivers/media/i2c/ad9389b.c64
-rw-r--r--drivers/media/i2c/adv7180.c120
-rw-r--r--drivers/media/i2c/adv7183.c4
-rw-r--r--drivers/media/i2c/adv7343.c4
-rw-r--r--drivers/media/i2c/adv7511.c66
-rw-r--r--drivers/media/i2c/adv7604.c1472
-rw-r--r--drivers/media/i2c/adv7842.c183
-rw-r--r--drivers/media/i2c/bt819.c2
-rw-r--r--drivers/media/i2c/cx25840/cx25840-core.c4
-rw-r--r--drivers/media/i2c/ir-kbd-i2c.c4
-rw-r--r--drivers/media/i2c/ks0127.c6
-rw-r--r--drivers/media/i2c/lm3560.c22
-rw-r--r--drivers/media/i2c/lm3646.c414
-rw-r--r--drivers/media/i2c/m5mols/m5mols_capture.c2
-rw-r--r--drivers/media/i2c/ml86v7667.c2
-rw-r--r--drivers/media/i2c/msp3400-driver.c2
-rw-r--r--drivers/media/i2c/mt9p031.c106
-rw-r--r--drivers/media/i2c/mt9t001.c229
-rw-r--r--drivers/media/i2c/mt9v011.c4
-rw-r--r--drivers/media/i2c/mt9v032.c10
-rw-r--r--drivers/media/i2c/ov7670.c2
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-core.c207
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-spi.c6
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3.h4
-rw-r--r--drivers/media/i2c/s5k5baf.c33
-rw-r--r--drivers/media/i2c/s5k6a3.c389
-rw-r--r--drivers/media/i2c/saa6752hs.c2
-rw-r--r--drivers/media/i2c/saa7110.c2
-rw-r--r--drivers/media/i2c/saa7115.c2
-rw-r--r--drivers/media/i2c/saa717x.c2
-rw-r--r--drivers/media/i2c/saa7191.c2
-rw-r--r--drivers/media/i2c/smiapp-pll.h2
-rw-r--r--drivers/media/i2c/smiapp/smiapp-core.c57
-rw-r--r--drivers/media/i2c/smiapp/smiapp-quirk.c55
-rw-r--r--drivers/media/i2c/smiapp/smiapp-quirk.h24
-rw-r--r--drivers/media/i2c/smiapp/smiapp-reg-defs.h8
-rw-r--r--drivers/media/i2c/smiapp/smiapp-regs.c89
-rw-r--r--drivers/media/i2c/smiapp/smiapp-regs.h19
-rw-r--r--drivers/media/i2c/soc_camera/tw9910.c11
-rw-r--r--drivers/media/i2c/sony-btf-mpx.c10
-rw-r--r--drivers/media/i2c/sr030pc30.c2
-rw-r--r--drivers/media/i2c/ths8200.c36
-rw-r--r--drivers/media/i2c/tvaudio.c6
-rw-r--r--drivers/media/i2c/tvp514x.c5
-rw-r--r--drivers/media/i2c/tvp5150.c14
-rw-r--r--drivers/media/i2c/tvp7002.c8
-rw-r--r--drivers/media/i2c/tw2804.c2
-rw-r--r--drivers/media/i2c/tw9903.c2
-rw-r--r--drivers/media/i2c/tw9906.c2
-rw-r--r--drivers/media/i2c/vp27smpx.c6
-rw-r--r--drivers/media/i2c/vpx3220.c2
53 files changed, 2828 insertions, 929 deletions
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 4aa9c5311cc..441053be7f5 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -196,7 +196,7 @@ config VIDEO_ADV7183
config VIDEO_ADV7604
tristate "Analog Devices ADV7604 decoder"
- depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
+ depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
---help---
Support for the Analog Devices ADV7604 video decoder.
@@ -208,7 +208,7 @@ config VIDEO_ADV7604
config VIDEO_ADV7842
tristate "Analog Devices ADV7842 decoder"
- depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
+ depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
---help---
Support for the Analog Devices ADV7842 video decoder.
@@ -431,7 +431,7 @@ config VIDEO_ADV7393
config VIDEO_ADV7511
tristate "Analog Devices ADV7511 encoder"
- depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
+ depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
---help---
Support for the Analog Devices ADV7511 video encoder.
@@ -579,6 +579,14 @@ config VIDEO_S5K6AA
This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M
camera sensor with an embedded SoC image signal processor.
+config VIDEO_S5K6A3
+ tristate "Samsung S5K6A3 sensor support"
+ depends on MEDIA_CAMERA_SUPPORT
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ ---help---
+ This is a V4L2 sensor-level driver for Samsung S5K6A3 raw
+ camera sensor.
+
config VIDEO_S5K4ECGX
tristate "Samsung S5K4ECGX sensor support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
@@ -629,6 +637,15 @@ config VIDEO_LM3560
This is a driver for the lm3560 dual flash controllers. It controls
flash, torch LEDs.
+config VIDEO_LM3646
+ tristate "LM3646 dual flash driver support"
+ depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
+ depends on MEDIA_CAMERA_SUPPORT
+ select REGMAP_I2C
+ ---help---
+ This is a driver for the lm3646 dual flash controllers. It controls
+ flash, torch LEDs.
+
comment "Video improvement chips"
config VIDEO_UPD64031A
@@ -659,6 +676,7 @@ comment "Audio/Video compression chips"
config VIDEO_SAA6752HS
tristate "Philips SAA6752HS MPEG-2 Audio/Video Encoder"
depends on VIDEO_V4L2 && I2C
+ select CRC32
---help---
Support for the Philips SAA6752HS MPEG-2 video and MPEG-audio/AC-3
audio encoder with multiplexer.
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 48888ae876f..01ae9328e58 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -66,12 +66,14 @@ obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o
obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o
obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o
+obj-$(CONFIG_VIDEO_S5K6A3) += s5k6a3.o
obj-$(CONFIG_VIDEO_S5K4ECGX) += s5k4ecgx.o
obj-$(CONFIG_VIDEO_S5K5BAF) += s5k5baf.o
obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/
obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o
obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o
obj-$(CONFIG_VIDEO_LM3560) += lm3560.o
+obj-$(CONFIG_VIDEO_LM3646) += lm3646.o
obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o
obj-$(CONFIG_VIDEO_AK881X) += ak881x.o
obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c
index 83225d6a0dd..fada1756620 100644
--- a/drivers/media/i2c/ad9389b.c
+++ b/drivers/media/i2c/ad9389b.c
@@ -571,35 +571,6 @@ static const struct v4l2_subdev_core_ops ad9389b_core_ops = {
.interrupt_service_routine = ad9389b_isr,
};
-/* ------------------------------ PAD OPS ------------------------------ */
-
-static int ad9389b_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid)
-{
- struct ad9389b_state *state = get_ad9389b_state(sd);
-
- if (edid->pad != 0)
- return -EINVAL;
- if (edid->blocks == 0 || edid->blocks > 256)
- return -EINVAL;
- if (!edid->edid)
- return -EINVAL;
- if (!state->edid.segments) {
- v4l2_dbg(1, debug, sd, "EDID segment 0 not found\n");
- return -ENODATA;
- }
- if (edid->start_block >= state->edid.segments * 2)
- return -E2BIG;
- if (edid->blocks + edid->start_block >= state->edid.segments * 2)
- edid->blocks = state->edid.segments * 2 - edid->start_block;
- memcpy(edid->edid, &state->edid.data[edid->start_block * 128],
- 128 * edid->blocks);
- return 0;
-}
-
-static const struct v4l2_subdev_pad_ops ad9389b_pad_ops = {
- .get_edid = ad9389b_get_edid,
-};
-
/* ------------------------------ VIDEO OPS ------------------------------ */
/* Enable/disable ad9389b output */
@@ -678,6 +649,9 @@ static int ad9389b_g_dv_timings(struct v4l2_subdev *sd,
static int ad9389b_enum_dv_timings(struct v4l2_subdev *sd,
struct v4l2_enum_dv_timings *timings)
{
+ if (timings->pad != 0)
+ return -EINVAL;
+
return v4l2_enum_dv_timings_cap(timings, &ad9389b_timings_cap,
NULL, NULL);
}
@@ -685,6 +659,9 @@ static int ad9389b_enum_dv_timings(struct v4l2_subdev *sd,
static int ad9389b_dv_timings_cap(struct v4l2_subdev *sd,
struct v4l2_dv_timings_cap *cap)
{
+ if (cap->pad != 0)
+ return -EINVAL;
+
*cap = ad9389b_timings_cap;
return 0;
}
@@ -693,10 +670,39 @@ static const struct v4l2_subdev_video_ops ad9389b_video_ops = {
.s_stream = ad9389b_s_stream,
.s_dv_timings = ad9389b_s_dv_timings,
.g_dv_timings = ad9389b_g_dv_timings,
+};
+
+/* ------------------------------ PAD OPS ------------------------------ */
+
+static int ad9389b_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
+{
+ struct ad9389b_state *state = get_ad9389b_state(sd);
+
+ if (edid->pad != 0)
+ return -EINVAL;
+ if (edid->blocks == 0 || edid->blocks > 256)
+ return -EINVAL;
+ if (!state->edid.segments) {
+ v4l2_dbg(1, debug, sd, "EDID segment 0 not found\n");
+ return -ENODATA;
+ }
+ if (edid->start_block >= state->edid.segments * 2)
+ return -E2BIG;
+ if (edid->blocks + edid->start_block >= state->edid.segments * 2)
+ edid->blocks = state->edid.segments * 2 - edid->start_block;
+ memcpy(edid->edid, &state->edid.data[edid->start_block * 128],
+ 128 * edid->blocks);
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops ad9389b_pad_ops = {
+ .get_edid = ad9389b_get_edid,
.enum_dv_timings = ad9389b_enum_dv_timings,
.dv_timings_cap = ad9389b_dv_timings_cap,
};
+/* ------------------------------ AUDIO OPS ------------------------------ */
+
static int ad9389b_s_audio_stream(struct v4l2_subdev *sd, int enable)
{
v4l2_dbg(1, debug, sd, "%s: %sable\n", __func__, (enable ? "en" : "dis"));
diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c
index d7d99f1c69e..ac1cdbe251a 100644
--- a/drivers/media/i2c/adv7180.c
+++ b/drivers/media/i2c/adv7180.c
@@ -123,11 +123,11 @@
struct adv7180_state {
struct v4l2_ctrl_handler ctrl_hdl;
struct v4l2_subdev sd;
- struct work_struct work;
struct mutex mutex; /* mutual excl. when accessing chip */
int irq;
v4l2_std_id curr_norm;
bool autodetect;
+ bool powered;
u8 input;
};
#define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler, \
@@ -312,6 +312,37 @@ out:
return ret;
}
+static int adv7180_set_power(struct adv7180_state *state,
+ struct i2c_client *client, bool on)
+{
+ u8 val;
+
+ if (on)
+ val = ADV7180_PWR_MAN_ON;
+ else
+ val = ADV7180_PWR_MAN_OFF;
+
+ return i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, val);
+}
+
+static int adv7180_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct adv7180_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ ret = mutex_lock_interruptible(&state->mutex);
+ if (ret)
+ return ret;
+
+ ret = adv7180_set_power(state, client, on);
+ if (ret == 0)
+ state->powered = on;
+
+ mutex_unlock(&state->mutex);
+ return ret;
+}
+
static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = to_adv7180_sd(ctrl);
@@ -430,6 +461,7 @@ static int adv7180_g_mbus_config(struct v4l2_subdev *sd,
}
static const struct v4l2_subdev_video_ops adv7180_video_ops = {
+ .s_std = adv7180_s_std,
.querystd = adv7180_querystd,
.g_input_status = adv7180_g_input_status,
.s_routing = adv7180_s_routing,
@@ -441,7 +473,7 @@ static const struct v4l2_subdev_video_ops adv7180_video_ops = {
};
static const struct v4l2_subdev_core_ops adv7180_core_ops = {
- .s_std = adv7180_s_std,
+ .s_power = adv7180_s_power,
};
static const struct v4l2_subdev_ops adv7180_ops = {
@@ -449,10 +481,9 @@ static const struct v4l2_subdev_ops adv7180_ops = {
.video = &adv7180_video_ops,
};
-static void adv7180_work(struct work_struct *work)
+static irqreturn_t adv7180_irq(int irq, void *devid)
{
- struct adv7180_state *state = container_of(work, struct adv7180_state,
- work);
+ struct adv7180_state *state = devid;
struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
u8 isr3;
@@ -468,17 +499,6 @@ static void adv7180_work(struct work_struct *work)
__adv7180_status(client, NULL, &state->curr_norm);
mutex_unlock(&state->mutex);
- enable_irq(state->irq);
-}
-
-static irqreturn_t adv7180_irq(int irq, void *devid)
-{
- struct adv7180_state *state = devid;
-
- schedule_work(&state->work);
-
- disable_irq_nosync(state->irq);
-
return IRQ_HANDLED;
}
@@ -533,48 +553,52 @@ static int init_device(struct i2c_client *client, struct adv7180_state *state)
/* register for interrupts */
if (state->irq > 0) {
- ret = request_irq(state->irq, adv7180_irq, 0, KBUILD_MODNAME,
- state);
+ ret = request_threaded_irq(state->irq, NULL, adv7180_irq,
+ IRQF_ONESHOT, KBUILD_MODNAME, state);
if (ret)
return ret;
ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
ADV7180_ADI_CTRL_IRQ_SPACE);
if (ret < 0)
- return ret;
+ goto err;
/* config the Interrupt pin to be active low */
ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI,
ADV7180_ICONF1_ACTIVE_LOW |
ADV7180_ICONF1_PSYNC_ONLY);
if (ret < 0)
- return ret;
+ goto err;
ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0);
if (ret < 0)
- return ret;
+ goto err;
ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0);
if (ret < 0)
- return ret;
+ goto err;
/* enable AD change interrupts interrupts */
ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI,
ADV7180_IRQ3_AD_CHANGE);
if (ret < 0)
- return ret;
+ goto err;
ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0);
if (ret < 0)
- return ret;
+ goto err;
ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
0);
if (ret < 0)
- return ret;
+ goto err;
}
return 0;
+
+err:
+ free_irq(state->irq, state);
+ return ret;
}
static int adv7180_probe(struct i2c_client *client,
@@ -598,9 +622,9 @@ static int adv7180_probe(struct i2c_client *client,
}
state->irq = client->irq;
- INIT_WORK(&state->work, adv7180_work);
mutex_init(&state->mutex);
state->autodetect = true;
+ state->powered = true;
state->input = 0;
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
@@ -611,15 +635,21 @@ static int adv7180_probe(struct i2c_client *client,
ret = init_device(client, state);
if (ret)
goto err_free_ctrl;
+
+ ret = v4l2_async_register_subdev(sd);
+ if (ret)
+ goto err_free_irq;
+
return 0;
+err_free_irq:
+ if (state->irq > 0)
+ free_irq(client->irq, state);
err_free_ctrl:
adv7180_exit_controls(state);
err_unreg_subdev:
mutex_destroy(&state->mutex);
- v4l2_device_unregister_subdev(sd);
err:
- printk(KERN_ERR KBUILD_MODNAME ": Failed to probe: %d\n", ret);
return ret;
}
@@ -628,20 +658,14 @@ static int adv7180_remove(struct i2c_client *client)
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct adv7180_state *state = to_state(sd);
- if (state->irq > 0) {
+ v4l2_async_unregister_subdev(sd);
+
+ if (state->irq > 0)
free_irq(client->irq, state);
- if (cancel_work_sync(&state->work)) {
- /*
- * Work was pending, therefore we need to enable
- * IRQ here to balance the disable_irq() done in the
- * interrupt handler.
- */
- enable_irq(state->irq);
- }
- }
- mutex_destroy(&state->mutex);
v4l2_device_unregister_subdev(sd);
+ adv7180_exit_controls(state);
+ mutex_destroy(&state->mutex);
return 0;
}
@@ -654,13 +678,10 @@ static const struct i2c_device_id adv7180_id[] = {
static int adv7180_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
- int ret;
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct adv7180_state *state = to_state(sd);
- ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG,
- ADV7180_PWR_MAN_OFF);
- if (ret < 0)
- return ret;
- return 0;
+ return adv7180_set_power(state, client, false);
}
static int adv7180_resume(struct device *dev)
@@ -670,10 +691,11 @@ static int adv7180_resume(struct device *dev)
struct adv7180_state *state = to_state(sd);
int ret;
- ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG,
- ADV7180_PWR_MAN_ON);
- if (ret < 0)
- return ret;
+ if (state->powered) {
+ ret = adv7180_set_power(state, client, true);
+ if (ret)
+ return ret;
+ }
ret = init_device(client, state);
if (ret < 0)
return ret;
diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c
index d45e0e3a781..df461b07b2f 100644
--- a/drivers/media/i2c/adv7183.c
+++ b/drivers/media/i2c/adv7183.c
@@ -501,8 +501,6 @@ static const struct v4l2_ctrl_ops adv7183_ctrl_ops = {
static const struct v4l2_subdev_core_ops adv7183_core_ops = {
.log_status = adv7183_log_status,
- .g_std = adv7183_g_std,
- .s_std = adv7183_s_std,
.reset = adv7183_reset,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = adv7183_g_register,
@@ -511,6 +509,8 @@ static const struct v4l2_subdev_core_ops adv7183_core_ops = {
};
static const struct v4l2_subdev_video_ops adv7183_video_ops = {
+ .g_std = adv7183_g_std,
+ .s_std = adv7183_s_std,
.s_routing = adv7183_s_routing,
.querystd = adv7183_querystd,
.g_input_status = adv7183_g_input_status,
diff --git a/drivers/media/i2c/adv7343.c b/drivers/media/i2c/adv7343.c
index d4e15a617c3..9d38f7b36cd 100644
--- a/drivers/media/i2c/adv7343.c
+++ b/drivers/media/i2c/adv7343.c
@@ -26,12 +26,12 @@
#include <linux/videodev2.h>
#include <linux/uaccess.h>
#include <linux/of.h>
+#include <linux/of_graph.h>
#include <media/adv7343.h>
#include <media/v4l2-async.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
-#include <media/v4l2-of.h>
#include "adv7343_regs.h"
@@ -410,7 +410,7 @@ adv7343_get_pdata(struct i2c_client *client)
if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node)
return client->dev.platform_data;
- np = v4l2_of_get_next_endpoint(client->dev.of_node, NULL);
+ np = of_graph_get_next_endpoint(client->dev.of_node, NULL);
if (!np)
return NULL;
diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c
index ee618942cb8..f98acf4aafd 100644
--- a/drivers/media/i2c/adv7511.c
+++ b/drivers/media/i2c/adv7511.c
@@ -597,34 +597,6 @@ static int adv7511_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
return 0;
}
-static int adv7511_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid)
-{
- struct adv7511_state *state = get_adv7511_state(sd);
-
- if (edid->pad != 0)
- return -EINVAL;
- if ((edid->blocks == 0) || (edid->blocks > 256))
- return -EINVAL;
- if (!edid->edid)
- return -EINVAL;
- if (!state->edid.segments) {
- v4l2_dbg(1, debug, sd, "EDID segment 0 not found\n");
- return -ENODATA;
- }
- if (edid->start_block >= state->edid.segments * 2)
- return -E2BIG;
- if ((edid->blocks + edid->start_block) >= state->edid.segments * 2)
- edid->blocks = state->edid.segments * 2 - edid->start_block;
-
- memcpy(edid->edid, &state->edid.data[edid->start_block * 128],
- 128 * edid->blocks);
- return 0;
-}
-
-static const struct v4l2_subdev_pad_ops adv7511_pad_ops = {
- .get_edid = adv7511_get_edid,
-};
-
static const struct v4l2_subdev_core_ops adv7511_core_ops = {
.log_status = adv7511_log_status,
#ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -700,12 +672,18 @@ static int adv7511_g_dv_timings(struct v4l2_subdev *sd,
static int adv7511_enum_dv_timings(struct v4l2_subdev *sd,
struct v4l2_enum_dv_timings *timings)
{
+ if (timings->pad != 0)
+ return -EINVAL;
+
return v4l2_enum_dv_timings_cap(timings, &adv7511_timings_cap, NULL, NULL);
}
static int adv7511_dv_timings_cap(struct v4l2_subdev *sd,
struct v4l2_dv_timings_cap *cap)
{
+ if (cap->pad != 0)
+ return -EINVAL;
+
*cap = adv7511_timings_cap;
return 0;
}
@@ -714,8 +692,6 @@ static const struct v4l2_subdev_video_ops adv7511_video_ops = {
.s_stream = adv7511_s_stream,
.s_dv_timings = adv7511_s_dv_timings,
.g_dv_timings = adv7511_g_dv_timings,
- .enum_dv_timings = adv7511_enum_dv_timings,
- .dv_timings_cap = adv7511_dv_timings_cap,
};
/* ------------------------------ AUDIO OPS ------------------------------ */
@@ -797,6 +773,36 @@ static const struct v4l2_subdev_audio_ops adv7511_audio_ops = {
.s_routing = adv7511_s_routing,
};
+/* ---------------------------- PAD OPS ------------------------------------- */
+
+static int adv7511_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
+{
+ struct adv7511_state *state = get_adv7511_state(sd);
+
+ if (edid->pad != 0)
+ return -EINVAL;
+ if ((edid->blocks == 0) || (edid->blocks > 256))
+ return -EINVAL;
+ if (!state->edid.segments) {
+ v4l2_dbg(1, debug, sd, "EDID segment 0 not found\n");
+ return -ENODATA;
+ }
+ if (edid->start_block >= state->edid.segments * 2)
+ return -E2BIG;
+ if ((edid->blocks + edid->start_block) >= state->edid.segments * 2)
+ edid->blocks = state->edid.segments * 2 - edid->start_block;
+
+ memcpy(edid->edid, &state->edid.data[edid->start_block * 128],
+ 128 * edid->blocks);
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops adv7511_pad_ops = {
+ .get_edid = adv7511_get_edid,
+ .enum_dv_timings = adv7511_enum_dv_timings,
+ .dv_timings_cap = adv7511_dv_timings_cap,
+};
+
/* --------------------- SUBDEV OPS --------------------------------------- */
static const struct v4l2_subdev_ops adv7511_ops = {
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index 71c8570bd9e..1778d320272 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -27,19 +27,21 @@
* REF_03 - Analog devices, ADV7604, Hardware Manual, Rev. F, August 2010
*/
-
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
-#include <linux/i2c.h>
-#include <linux/delay.h>
+#include <linux/v4l2-dv-timings.h>
#include <linux/videodev2.h>
#include <linux/workqueue.h>
-#include <linux/v4l2-dv-timings.h>
-#include <media/v4l2-device.h>
+
+#include <media/adv7604.h>
#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-dv-timings.h>
-#include <media/adv7604.h>
+#include <media/v4l2-of.h>
static int debug;
module_param(debug, int, 0644);
@@ -53,6 +55,76 @@ MODULE_LICENSE("GPL");
/* ADV7604 system clock frequency */
#define ADV7604_fsc (28636360)
+#define ADV7604_RGB_OUT (1 << 1)
+
+#define ADV7604_OP_FORMAT_SEL_8BIT (0 << 0)
+#define ADV7604_OP_FORMAT_SEL_10BIT (1 << 0)
+#define ADV7604_OP_FORMAT_SEL_12BIT (2 << 0)
+
+#define ADV7604_OP_MODE_SEL_SDR_422 (0 << 5)
+#define ADV7604_OP_MODE_SEL_DDR_422 (1 << 5)
+#define ADV7604_OP_MODE_SEL_SDR_444 (2 << 5)
+#define ADV7604_OP_MODE_SEL_DDR_444 (3 << 5)
+#define ADV7604_OP_MODE_SEL_SDR_422_2X (4 << 5)
+#define ADV7604_OP_MODE_SEL_ADI_CM (5 << 5)
+
+#define ADV7604_OP_CH_SEL_GBR (0 << 5)
+#define ADV7604_OP_CH_SEL_GRB (1 << 5)
+#define ADV7604_OP_CH_SEL_BGR (2 << 5)
+#define ADV7604_OP_CH_SEL_RGB (3 << 5)
+#define ADV7604_OP_CH_SEL_BRG (4 << 5)
+#define ADV7604_OP_CH_SEL_RBG (5 << 5)
+
+#define ADV7604_OP_SWAP_CB_CR (1 << 0)
+
+enum adv7604_type {
+ ADV7604,
+ ADV7611,
+};
+
+struct adv7604_reg_seq {
+ unsigned int reg;
+ u8 val;
+};
+
+struct adv7604_format_info {
+ enum v4l2_mbus_pixelcode code;
+ u8 op_ch_sel;
+ bool rgb_out;
+ bool swap_cb_cr;
+ u8 op_format_sel;
+};
+
+struct adv7604_chip_info {
+ enum adv7604_type type;
+
+ bool has_afe;
+ unsigned int max_port;
+ unsigned int num_dv_ports;
+
+ unsigned int edid_enable_reg;
+ unsigned int edid_status_reg;
+ unsigned int lcf_reg;
+
+ unsigned int cable_det_mask;
+ unsigned int tdms_lock_mask;
+ unsigned int fmt_change_digital_mask;
+
+ const struct adv7604_format_info *formats;
+ unsigned int nformats;
+
+ void (*set_termination)(struct v4l2_subdev *sd, bool enable);
+ void (*setup_irqs)(struct v4l2_subdev *sd);
+ unsigned int (*read_hdmi_pixelclock)(struct v4l2_subdev *sd);
+ unsigned int (*read_cable_det)(struct v4l2_subdev *sd);
+
+ /* 0 = AFE, 1 = HDMI */
+ const struct adv7604_reg_seq *recommended_settings[2];
+ unsigned int num_recommended_settings[2];
+
+ unsigned long page_mask;
+};
+
/*
**********************************************************************
*
@@ -60,13 +132,24 @@ MODULE_LICENSE("GPL");
*
**********************************************************************
*/
+
struct adv7604_state {
+ const struct adv7604_chip_info *info;
struct adv7604_platform_data pdata;
+
+ struct gpio_desc *hpd_gpio[4];
+
struct v4l2_subdev sd;
- struct media_pad pad;
+ struct media_pad pads[ADV7604_PAD_MAX];
+ unsigned int source_pad;
+
struct v4l2_ctrl_handler hdl;
- enum adv7604_input_port selected_input;
+
+ enum adv7604_pad selected_input;
+
struct v4l2_dv_timings timings;
+ const struct adv7604_format_info *format;
+
struct {
u8 edid[256];
u32 present;
@@ -80,18 +163,7 @@ struct adv7604_state {
bool restart_stdi_once;
/* i2c clients */
- struct i2c_client *i2c_avlink;
- struct i2c_client *i2c_cec;
- struct i2c_client *i2c_infoframe;
- struct i2c_client *i2c_esdp;
- struct i2c_client *i2c_dpp;
- struct i2c_client *i2c_afe;
- struct i2c_client *i2c_repeater;
- struct i2c_client *i2c_edid;
- struct i2c_client *i2c_hdmi;
- struct i2c_client *i2c_test;
- struct i2c_client *i2c_cp;
- struct i2c_client *i2c_vdp;
+ struct i2c_client *i2c_clients[ADV7604_PAGE_MAX];
/* controls */
struct v4l2_ctrl *detect_tx_5v_ctrl;
@@ -101,6 +173,11 @@ struct adv7604_state {
struct v4l2_ctrl *rgb_quantization_range_ctrl;
};
+static bool adv7604_has_afe(struct adv7604_state *state)
+{
+ return state->info->has_afe;
+}
+
/* Supported CEA and DMT timings */
static const struct v4l2_dv_timings adv7604_timings[] = {
V4L2_DV_BT_CEA_720X480P59_94,
@@ -256,11 +333,6 @@ static inline struct adv7604_state *to_state(struct v4l2_subdev *sd)
return container_of(sd, struct adv7604_state, sd);
}
-static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
-{
- return &container_of(ctrl->handler, struct adv7604_state, hdl)->sd;
-}
-
static inline unsigned hblanking(const struct v4l2_bt_timings *t)
{
return V4L2_DV_BT_BLANKING_WIDTH(t);
@@ -298,14 +370,18 @@ static s32 adv_smbus_read_byte_data_check(struct i2c_client *client,
return -EIO;
}
-static s32 adv_smbus_read_byte_data(struct i2c_client *client, u8 command)
+static s32 adv_smbus_read_byte_data(struct adv7604_state *state,
+ enum adv7604_page page, u8 command)
{
- return adv_smbus_read_byte_data_check(client, command, true);
+ return adv_smbus_read_byte_data_check(state->i2c_clients[page],
+ command, true);
}
-static s32 adv_smbus_write_byte_data(struct i2c_client *client,
- u8 command, u8 value)
+static s32 adv_smbus_write_byte_data(struct adv7604_state *state,
+ enum adv7604_page page, u8 command,
+ u8 value)
{
+ struct i2c_client *client = state->i2c_clients[page];
union i2c_smbus_data data;
int err;
int i;
@@ -325,9 +401,11 @@ static s32 adv_smbus_write_byte_data(struct i2c_client *client,
return err;
}
-static s32 adv_smbus_write_i2c_block_data(struct i2c_client *client,
- u8 command, unsigned length, const u8 *values)
+static s32 adv_smbus_write_i2c_block_data(struct adv7604_state *state,
+ enum adv7604_page page, u8 command,
+ unsigned length, const u8 *values)
{
+ struct i2c_client *client = state->i2c_clients[page];
union i2c_smbus_data data;
if (length > I2C_SMBUS_BLOCK_MAX)
@@ -343,149 +421,150 @@ static s32 adv_smbus_write_i2c_block_data(struct i2c_client *client,
static inline int io_read(struct v4l2_subdev *sd, u8 reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct adv7604_state *state = to_state(sd);
- return adv_smbus_read_byte_data(client, reg);
+ return adv_smbus_read_byte_data(state, ADV7604_PAGE_IO, reg);
}
static inline int io_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct adv7604_state *state = to_state(sd);
- return adv_smbus_write_byte_data(client, reg, val);
+ return adv_smbus_write_byte_data(state, ADV7604_PAGE_IO, reg, val);
}
-static inline int io_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
+static inline int io_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
{
- return io_write(sd, reg, (io_read(sd, reg) & mask) | val);
+ return io_write(sd, reg, (io_read(sd, reg) & ~mask) | val);
}
static inline int avlink_read(struct v4l2_subdev *sd, u8 reg)
{
struct adv7604_state *state = to_state(sd);
- return adv_smbus_read_byte_data(state->i2c_avlink, reg);
+ return adv_smbus_read_byte_data(state, ADV7604_PAGE_AVLINK, reg);
}
static inline int avlink_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
struct adv7604_state *state = to_state(sd);
- return adv_smbus_write_byte_data(state->i2c_avlink, reg, val);
+ return adv_smbus_write_byte_data(state, ADV7604_PAGE_AVLINK, reg, val);
}
static inline int cec_read(struct v4l2_subdev *sd, u8 reg)
{
struct adv7604_state *state = to_state(sd);
- return adv_smbus_read_byte_data(state->i2c_cec, reg);
+ return adv_smbus_read_byte_data(state, ADV7604_PAGE_CEC, reg);
}
static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
struct adv7604_state *state = to_state(sd);
- return adv_smbus_write_byte_data(state->i2c_cec, reg, val);
+ return adv_smbus_write_byte_data(state, ADV7604_PAGE_CEC, reg, val);
}
-static inline int cec_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
+static inline int cec_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
{
- return cec_write(sd, reg, (cec_read(sd, reg) & mask) | val);
+ return cec_write(sd, reg, (cec_read(sd, reg) & ~mask) | val);
}
static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg)
{
struct adv7604_state *state = to_state(sd);
- return adv_smbus_read_byte_data(state->i2c_infoframe, reg);
+ return adv_smbus_read_byte_data(state, ADV7604_PAGE_INFOFRAME, reg);
}
static inline int infoframe_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
struct adv7604_state *state = to_state(sd);
- return adv_smbus_write_byte_data(state->i2c_infoframe, reg, val);
+ return adv_smbus_write_byte_data(state, ADV7604_PAGE_INFOFRAME,
+ reg, val);
}
static inline int esdp_read(struct v4l2_subdev *sd, u8 reg)
{
struct adv7604_state *state = to_state(sd);
- return adv_smbus_read_byte_data(state->i2c_esdp, reg);
+ return adv_smbus_read_byte_data(state, ADV7604_PAGE_ESDP, reg);
}
static inline int esdp_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
struct adv7604_state *state = to_state(sd);
- return adv_smbus_write_byte_data(state->i2c_esdp, reg, val);
+ return adv_smbus_write_byte_data(state, ADV7604_PAGE_ESDP, reg, val);
}
static inline int dpp_read(struct v4l2_subdev *sd, u8 reg)
{
struct adv7604_state *state = to_state(sd);
- return adv_smbus_read_byte_data(state->i2c_dpp, reg);
+ return adv_smbus_read_byte_data(state, ADV7604_PAGE_DPP, reg);
}
static inline int dpp_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
struct adv7604_state *state = to_state(sd);
- return adv_smbus_write_byte_data(state->i2c_dpp, reg, val);
+ return adv_smbus_write_byte_data(state, ADV7604_PAGE_DPP, reg, val);
}
static inline int afe_read(struct v4l2_subdev *sd, u8 reg)
{
struct adv7604_state *state = to_state(sd);
- return adv_smbus_read_byte_data(state->i2c_afe, reg);
+ return adv_smbus_read_byte_data(state, ADV7604_PAGE_AFE, reg);
}
static inline int afe_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
struct adv7604_state *state = to_state(sd);
- return adv_smbus_write_byte_data(state->i2c_afe, reg, val);
+ return adv_smbus_write_byte_data(state, ADV7604_PAGE_AFE, reg, val);
}
static inline int rep_read(struct v4l2_subdev *sd, u8 reg)
{
struct adv7604_state *state = to_state(sd);
- return adv_smbus_read_byte_data(state->i2c_repeater, reg);
+ return adv_smbus_read_byte_data(state, ADV7604_PAGE_REP, reg);
}
static inline int rep_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
struct adv7604_state *state = to_state(sd);
- return adv_smbus_write_byte_data(state->i2c_repeater, reg, val);
+ return adv_smbus_write_byte_data(state, ADV7604_PAGE_REP, reg, val);
}
-static inline int rep_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
+static inline int rep_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
{
- return rep_write(sd, reg, (rep_read(sd, reg) & mask) | val);
+ return rep_write(sd, reg, (rep_read(sd, reg) & ~mask) | val);
}
static inline int edid_read(struct v4l2_subdev *sd, u8 reg)
{
struct adv7604_state *state = to_state(sd);
- return adv_smbus_read_byte_data(state->i2c_edid, reg);
+ return adv_smbus_read_byte_data(state, ADV7604_PAGE_EDID, reg);
}
static inline int edid_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
struct adv7604_state *state = to_state(sd);
- return adv_smbus_write_byte_data(state->i2c_edid, reg, val);
+ return adv_smbus_write_byte_data(state, ADV7604_PAGE_EDID, reg, val);
}
static inline int edid_read_block(struct v4l2_subdev *sd, unsigned len, u8 *val)
{
struct adv7604_state *state = to_state(sd);
- struct i2c_client *client = state->i2c_edid;
+ struct i2c_client *client = state->i2c_clients[ADV7604_PAGE_EDID];
u8 msgbuf0[1] = { 0 };
u8 msgbuf1[256];
struct i2c_msg msg[2] = {
@@ -518,11 +597,25 @@ static inline int edid_write_block(struct v4l2_subdev *sd,
v4l2_dbg(2, debug, sd, "%s: write EDID block (%d byte)\n", __func__, len);
for (i = 0; !err && i < len; i += I2C_SMBUS_BLOCK_MAX)
- err = adv_smbus_write_i2c_block_data(state->i2c_edid, i,
- I2C_SMBUS_BLOCK_MAX, val + i);
+ err = adv_smbus_write_i2c_block_data(state, ADV7604_PAGE_EDID,
+ i, I2C_SMBUS_BLOCK_MAX, val + i);
return err;
}
+static void adv7604_set_hpd(struct adv7604_state *state, unsigned int hpd)
+{
+ unsigned int i;
+
+ for (i = 0; i < state->info->num_dv_ports; ++i) {
+ if (IS_ERR(state->hpd_gpio[i]))
+ continue;
+
+ gpiod_set_value_cansleep(state->hpd_gpio[i], hpd & BIT(i));
+ }
+
+ v4l2_subdev_notify(&state->sd, ADV7604_HOTPLUG, &hpd);
+}
+
static void adv7604_delayed_work_enable_hotplug(struct work_struct *work)
{
struct delayed_work *dwork = to_delayed_work(work);
@@ -532,73 +625,210 @@ static void adv7604_delayed_work_enable_hotplug(struct work_struct *work)
v4l2_dbg(2, debug, sd, "%s: enable hotplug\n", __func__);
- v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)&state->edid.present);
+ adv7604_set_hpd(state, state->edid.present);
}
static inline int hdmi_read(struct v4l2_subdev *sd, u8 reg)
{
struct adv7604_state *state = to_state(sd);
- return adv_smbus_read_byte_data(state->i2c_hdmi, reg);
+ return adv_smbus_read_byte_data(state, ADV7604_PAGE_HDMI, reg);
+}
+
+static u16 hdmi_read16(struct v4l2_subdev *sd, u8 reg, u16 mask)
+{
+ return ((hdmi_read(sd, reg) << 8) | hdmi_read(sd, reg + 1)) & mask;
}
static inline int hdmi_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
struct adv7604_state *state = to_state(sd);
- return adv_smbus_write_byte_data(state->i2c_hdmi, reg, val);
+ return adv_smbus_write_byte_data(state, ADV7604_PAGE_HDMI, reg, val);
}
-static inline int hdmi_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
+static inline int hdmi_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
{
- return hdmi_write(sd, reg, (hdmi_read(sd, reg) & mask) | val);
+ return hdmi_write(sd, reg, (hdmi_read(sd, reg) & ~mask) | val);
}
static inline int test_read(struct v4l2_subdev *sd, u8 reg)
{
struct adv7604_state *state = to_state(sd);
- return adv_smbus_read_byte_data(state->i2c_test, reg);
+ return adv_smbus_read_byte_data(state, ADV7604_PAGE_TEST, reg);
}
static inline int test_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
struct adv7604_state *state = to_state(sd);
- return adv_smbus_write_byte_data(state->i2c_test, reg, val);
+ return adv_smbus_write_byte_data(state, ADV7604_PAGE_TEST, reg, val);
}
static inline int cp_read(struct v4l2_subdev *sd, u8 reg)
{
struct adv7604_state *state = to_state(sd);
- return adv_smbus_read_byte_data(state->i2c_cp, reg);
+ return adv_smbus_read_byte_data(state, ADV7604_PAGE_CP, reg);
+}
+
+static u16 cp_read16(struct v4l2_subdev *sd, u8 reg, u16 mask)
+{
+ return ((cp_read(sd, reg) << 8) | cp_read(sd, reg + 1)) & mask;
}
static inline int cp_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
struct adv7604_state *state = to_state(sd);
- return adv_smbus_write_byte_data(state->i2c_cp, reg, val);
+ return adv_smbus_write_byte_data(state, ADV7604_PAGE_CP, reg, val);
}
-static inline int cp_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
+static inline int cp_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
{
- return cp_write(sd, reg, (cp_read(sd, reg) & mask) | val);
+ return cp_write(sd, reg, (cp_read(sd, reg) & ~mask) | val);
}
static inline int vdp_read(struct v4l2_subdev *sd, u8 reg)
{
struct adv7604_state *state = to_state(sd);
- return adv_smbus_read_byte_data(state->i2c_vdp, reg);
+ return adv_smbus_read_byte_data(state, ADV7604_PAGE_VDP, reg);
}
static inline int vdp_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
struct adv7604_state *state = to_state(sd);
- return adv_smbus_write_byte_data(state->i2c_vdp, reg, val);
+ return adv_smbus_write_byte_data(state, ADV7604_PAGE_VDP, reg, val);
+}
+
+#define ADV7604_REG(page, offset) (((page) << 8) | (offset))
+#define ADV7604_REG_SEQ_TERM 0xffff
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int adv7604_read_reg(struct v4l2_subdev *sd, unsigned int reg)
+{
+ struct adv7604_state *state = to_state(sd);
+ unsigned int page = reg >> 8;
+
+ if (!(BIT(page) & state->info->page_mask))
+ return -EINVAL;
+
+ reg &= 0xff;
+
+ return adv_smbus_read_byte_data(state, page, reg);
+}
+#endif
+
+static int adv7604_write_reg(struct v4l2_subdev *sd, unsigned int reg, u8 val)
+{
+ struct adv7604_state *state = to_state(sd);
+ unsigned int page = reg >> 8;
+
+ if (!(BIT(page) & state->info->page_mask))
+ return -EINVAL;
+
+ reg &= 0xff;
+
+ return adv_smbus_write_byte_data(state, page, reg, val);
+}
+
+static void adv7604_write_reg_seq(struct v4l2_subdev *sd,
+ const struct adv7604_reg_seq *reg_seq)
+{
+ unsigned int i;
+
+ for (i = 0; reg_seq[i].reg != ADV7604_REG_SEQ_TERM; i++)
+ adv7604_write_reg(sd, reg_seq[i].reg, reg_seq[i].val);
+}
+
+/* -----------------------------------------------------------------------------
+ * Format helpers
+ */
+
+static const struct adv7604_format_info adv7604_formats[] = {
+ { V4L2_MBUS_FMT_RGB888_1X24, ADV7604_OP_CH_SEL_RGB, true, false,
+ ADV7604_OP_MODE_SEL_SDR_444 | ADV7604_OP_FORMAT_SEL_8BIT },
+ { V4L2_MBUS_FMT_YUYV8_2X8, ADV7604_OP_CH_SEL_RGB, false, false,
+ ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT },
+ { V4L2_MBUS_FMT_YVYU8_2X8, ADV7604_OP_CH_SEL_RGB, false, true,
+ ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT },
+ { V4L2_MBUS_FMT_YUYV10_2X10, ADV7604_OP_CH_SEL_RGB, false, false,
+ ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_10BIT },
+ { V4L2_MBUS_FMT_YVYU10_2X10, ADV7604_OP_CH_SEL_RGB, false, true,
+ ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_10BIT },
+ { V4L2_MBUS_FMT_YUYV12_2X12, ADV7604_OP_CH_SEL_RGB, false, false,
+ ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT },
+ { V4L2_MBUS_FMT_YVYU12_2X12, ADV7604_OP_CH_SEL_RGB, false, true,
+ ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT },
+ { V4L2_MBUS_FMT_UYVY8_1X16, ADV7604_OP_CH_SEL_RBG, false, false,
+ ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT },
+ { V4L2_MBUS_FMT_VYUY8_1X16, ADV7604_OP_CH_SEL_RBG, false, true,
+ ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT },
+ { V4L2_MBUS_FMT_YUYV8_1X16, ADV7604_OP_CH_SEL_RGB, false, false,
+ ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT },
+ { V4L2_MBUS_FMT_YVYU8_1X16, ADV7604_OP_CH_SEL_RGB, false, true,
+ ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT },
+ { V4L2_MBUS_FMT_UYVY10_1X20, ADV7604_OP_CH_SEL_RBG, false, false,
+ ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT },
+ { V4L2_MBUS_FMT_VYUY10_1X20, ADV7604_OP_CH_SEL_RBG, false, true,
+ ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT },
+ { V4L2_MBUS_FMT_YUYV10_1X20, ADV7604_OP_CH_SEL_RGB, false, false,
+ ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT },
+ { V4L2_MBUS_FMT_YVYU10_1X20, ADV7604_OP_CH_SEL_RGB, false, true,
+ ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT },
+ { V4L2_MBUS_FMT_UYVY12_1X24, ADV7604_OP_CH_SEL_RBG, false, false,
+ ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT },
+ { V4L2_MBUS_FMT_VYUY12_1X24, ADV7604_OP_CH_SEL_RBG, false, true,
+ ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT },
+ { V4L2_MBUS_FMT_YUYV12_1X24, ADV7604_OP_CH_SEL_RGB, false, false,
+ ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT },
+ { V4L2_MBUS_FMT_YVYU12_1X24, ADV7604_OP_CH_SEL_RGB, false, true,
+ ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT },
+};
+
+static const struct adv7604_format_info adv7611_formats[] = {
+ { V4L2_MBUS_FMT_RGB888_1X24, ADV7604_OP_CH_SEL_RGB, true, false,
+ ADV7604_OP_MODE_SEL_SDR_444 | ADV7604_OP_FORMAT_SEL_8BIT },
+ { V4L2_MBUS_FMT_YUYV8_2X8, ADV7604_OP_CH_SEL_RGB, false, false,
+ ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT },
+ { V4L2_MBUS_FMT_YVYU8_2X8, ADV7604_OP_CH_SEL_RGB, false, true,
+ ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT },
+ { V4L2_MBUS_FMT_YUYV12_2X12, ADV7604_OP_CH_SEL_RGB, false, false,
+ ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT },
+ { V4L2_MBUS_FMT_YVYU12_2X12, ADV7604_OP_CH_SEL_RGB, false, true,
+ ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT },
+ { V4L2_MBUS_FMT_UYVY8_1X16, ADV7604_OP_CH_SEL_RBG, false, false,
+ ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT },
+ { V4L2_MBUS_FMT_VYUY8_1X16, ADV7604_OP_CH_SEL_RBG, false, true,
+ ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT },
+ { V4L2_MBUS_FMT_YUYV8_1X16, ADV7604_OP_CH_SEL_RGB, false, false,
+ ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT },
+ { V4L2_MBUS_FMT_YVYU8_1X16, ADV7604_OP_CH_SEL_RGB, false, true,
+ ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT },
+ { V4L2_MBUS_FMT_UYVY12_1X24, ADV7604_OP_CH_SEL_RBG, false, false,
+ ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT },
+ { V4L2_MBUS_FMT_VYUY12_1X24, ADV7604_OP_CH_SEL_RBG, false, true,
+ ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT },
+ { V4L2_MBUS_FMT_YUYV12_1X24, ADV7604_OP_CH_SEL_RGB, false, false,
+ ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT },
+ { V4L2_MBUS_FMT_YVYU12_1X24, ADV7604_OP_CH_SEL_RGB, false, true,
+ ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT },
+};
+
+static const struct adv7604_format_info *
+adv7604_format_info(struct adv7604_state *state, enum v4l2_mbus_pixelcode code)
+{
+ unsigned int i;
+
+ for (i = 0; i < state->info->nformats; ++i) {
+ if (state->info->formats[i].code == code)
+ return &state->info->formats[i];
+ }
+
+ return NULL;
}
/* ----------------------------------------------------------------------- */
@@ -607,18 +837,18 @@ static inline bool is_analog_input(struct v4l2_subdev *sd)
{
struct adv7604_state *state = to_state(sd);
- return state->selected_input == ADV7604_INPUT_VGA_RGB ||
- state->selected_input == ADV7604_INPUT_VGA_COMP;
+ return state->selected_input == ADV7604_PAD_VGA_RGB ||
+ state->selected_input == ADV7604_PAD_VGA_COMP;
}
static inline bool is_digital_input(struct v4l2_subdev *sd)
{
struct adv7604_state *state = to_state(sd);
- return state->selected_input == ADV7604_INPUT_HDMI_PORT_A ||
- state->selected_input == ADV7604_INPUT_HDMI_PORT_B ||
- state->selected_input == ADV7604_INPUT_HDMI_PORT_C ||
- state->selected_input == ADV7604_INPUT_HDMI_PORT_D;
+ return state->selected_input == ADV7604_PAD_HDMI_PORT_A ||
+ state->selected_input == ADV7604_PAD_HDMI_PORT_B ||
+ state->selected_input == ADV7604_PAD_HDMI_PORT_C ||
+ state->selected_input == ADV7604_PAD_HDMI_PORT_D;
}
/* ----------------------------------------------------------------------- */
@@ -644,119 +874,61 @@ static void adv7604_inv_register(struct v4l2_subdev *sd)
static int adv7604_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
{
- reg->size = 1;
- switch (reg->reg >> 8) {
- case 0:
- reg->val = io_read(sd, reg->reg & 0xff);
- break;
- case 1:
- reg->val = avlink_read(sd, reg->reg & 0xff);
- break;
- case 2:
- reg->val = cec_read(sd, reg->reg & 0xff);
- break;
- case 3:
- reg->val = infoframe_read(sd, reg->reg & 0xff);
- break;
- case 4:
- reg->val = esdp_read(sd, reg->reg & 0xff);
- break;
- case 5:
- reg->val = dpp_read(sd, reg->reg & 0xff);
- break;
- case 6:
- reg->val = afe_read(sd, reg->reg & 0xff);
- break;
- case 7:
- reg->val = rep_read(sd, reg->reg & 0xff);
- break;
- case 8:
- reg->val = edid_read(sd, reg->reg & 0xff);
- break;
- case 9:
- reg->val = hdmi_read(sd, reg->reg & 0xff);
- break;
- case 0xa:
- reg->val = test_read(sd, reg->reg & 0xff);
- break;
- case 0xb:
- reg->val = cp_read(sd, reg->reg & 0xff);
- break;
- case 0xc:
- reg->val = vdp_read(sd, reg->reg & 0xff);
- break;
- default:
+ int ret;
+
+ ret = adv7604_read_reg(sd, reg->reg);
+ if (ret < 0) {
v4l2_info(sd, "Register %03llx not supported\n", reg->reg);
adv7604_inv_register(sd);
- break;
+ return ret;
}
+
+ reg->size = 1;
+ reg->val = ret;
+
return 0;
}
static int adv7604_s_register(struct v4l2_subdev *sd,
const struct v4l2_dbg_register *reg)
{
- u8 val = reg->val & 0xff;
+ int ret;
- switch (reg->reg >> 8) {
- case 0:
- io_write(sd, reg->reg & 0xff, val);
- break;
- case 1:
- avlink_write(sd, reg->reg & 0xff, val);
- break;
- case 2:
- cec_write(sd, reg->reg & 0xff, val);
- break;
- case 3:
- infoframe_write(sd, reg->reg & 0xff, val);
- break;
- case 4:
- esdp_write(sd, reg->reg & 0xff, val);
- break;
- case 5:
- dpp_write(sd, reg->reg & 0xff, val);
- break;
- case 6:
- afe_write(sd, reg->reg & 0xff, val);
- break;
- case 7:
- rep_write(sd, reg->reg & 0xff, val);
- break;
- case 8:
- edid_write(sd, reg->reg & 0xff, val);
- break;
- case 9:
- hdmi_write(sd, reg->reg & 0xff, val);
- break;
- case 0xa:
- test_write(sd, reg->reg & 0xff, val);
- break;
- case 0xb:
- cp_write(sd, reg->reg & 0xff, val);
- break;
- case 0xc:
- vdp_write(sd, reg->reg & 0xff, val);
- break;
- default:
+ ret = adv7604_write_reg(sd, reg->reg, reg->val);
+ if (ret < 0) {
v4l2_info(sd, "Register %03llx not supported\n", reg->reg);
adv7604_inv_register(sd);
- break;
+ return ret;
}
+
return 0;
}
#endif
+static unsigned int adv7604_read_cable_det(struct v4l2_subdev *sd)
+{
+ u8 value = io_read(sd, 0x6f);
+
+ return ((value & 0x10) >> 4)
+ | ((value & 0x08) >> 2)
+ | ((value & 0x04) << 0)
+ | ((value & 0x02) << 2);
+}
+
+static unsigned int adv7611_read_cable_det(struct v4l2_subdev *sd)
+{
+ u8 value = io_read(sd, 0x6f);
+
+ return value & 1;
+}
+
static int adv7604_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd)
{
struct adv7604_state *state = to_state(sd);
- u8 reg_io_6f = io_read(sd, 0x6f);
+ const struct adv7604_chip_info *info = state->info;
return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl,
- ((reg_io_6f & 0x10) >> 4) |
- ((reg_io_6f & 0x08) >> 2) |
- (reg_io_6f & 0x04) |
- ((reg_io_6f & 0x02) << 2));
+ info->read_cable_det(sd));
}
static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd,
@@ -787,11 +959,13 @@ static int configure_predefined_video_timings(struct v4l2_subdev *sd,
v4l2_dbg(1, debug, sd, "%s", __func__);
- /* reset to default values */
- io_write(sd, 0x16, 0x43);
- io_write(sd, 0x17, 0x5a);
+ if (adv7604_has_afe(state)) {
+ /* reset to default values */
+ io_write(sd, 0x16, 0x43);
+ io_write(sd, 0x17, 0x5a);
+ }
/* disable embedded syncs for auto graphics mode */
- cp_write_and_or(sd, 0x81, 0xef, 0x00);
+ cp_write_clr_set(sd, 0x81, 0x10, 0x00);
cp_write(sd, 0x8f, 0x00);
cp_write(sd, 0x90, 0x00);
cp_write(sd, 0xa2, 0x00);
@@ -829,7 +1003,6 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd,
const struct v4l2_bt_timings *bt)
{
struct adv7604_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
u32 width = htotal(bt);
u32 height = vtotal(bt);
u16 cp_start_sav = bt->hsync + bt->hbackporch - 4;
@@ -850,12 +1023,13 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd,
io_write(sd, 0x00, 0x07); /* video std */
io_write(sd, 0x01, 0x02); /* prim mode */
/* enable embedded syncs for auto graphics mode */
- cp_write_and_or(sd, 0x81, 0xef, 0x10);
+ cp_write_clr_set(sd, 0x81, 0x10, 0x10);
/* Should only be set in auto-graphics mode [REF_02, p. 91-92] */
/* setup PLL_DIV_MAN_EN and PLL_DIV_RATIO */
/* IO-map reg. 0x16 and 0x17 should be written in sequence */
- if (adv_smbus_write_i2c_block_data(client, 0x16, 2, pll))
+ if (adv_smbus_write_i2c_block_data(state, ADV7604_PAGE_IO,
+ 0x16, 2, pll))
v4l2_err(sd, "writing to reg 0x16 and 0x17 failed\n");
/* active video - horizontal timing */
@@ -906,7 +1080,8 @@ static void adv7604_set_offset(struct v4l2_subdev *sd, bool auto_offset, u16 off
offset_buf[3] = offset_c & 0x0ff;
/* Registers must be written in this order with no i2c access in between */
- if (adv_smbus_write_i2c_block_data(state->i2c_cp, 0x77, 4, offset_buf))
+ if (adv_smbus_write_i2c_block_data(state, ADV7604_PAGE_CP,
+ 0x77, 4, offset_buf))
v4l2_err(sd, "%s: i2c error writing to CP reg 0x77, 0x78, 0x79, 0x7a\n", __func__);
}
@@ -935,7 +1110,8 @@ static void adv7604_set_gain(struct v4l2_subdev *sd, bool auto_gain, u16 gain_a,
gain_buf[3] = ((gain_c & 0x0ff));
/* Registers must be written in this order with no i2c access in between */
- if (adv_smbus_write_i2c_block_data(state->i2c_cp, 0x73, 4, gain_buf))
+ if (adv_smbus_write_i2c_block_data(state, ADV7604_PAGE_CP,
+ 0x73, 4, gain_buf))
v4l2_err(sd, "%s: i2c error writing to CP reg 0x73, 0x74, 0x75, 0x76\n", __func__);
}
@@ -954,24 +1130,24 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd)
switch (state->rgb_quantization_range) {
case V4L2_DV_RGB_RANGE_AUTO:
- if (state->selected_input == ADV7604_INPUT_VGA_RGB) {
+ if (state->selected_input == ADV7604_PAD_VGA_RGB) {
/* Receiving analog RGB signal
* Set RGB full range (0-255) */
- io_write_and_or(sd, 0x02, 0x0f, 0x10);
+ io_write_clr_set(sd, 0x02, 0xf0, 0x10);
break;
}
- if (state->selected_input == ADV7604_INPUT_VGA_COMP) {
+ if (state->selected_input == ADV7604_PAD_VGA_COMP) {
/* Receiving analog YPbPr signal
* Set automode */
- io_write_and_or(sd, 0x02, 0x0f, 0xf0);
+ io_write_clr_set(sd, 0x02, 0xf0, 0xf0);
break;
}
if (hdmi_signal) {
/* Receiving HDMI signal
* Set automode */
- io_write_and_or(sd, 0x02, 0x0f, 0xf0);
+ io_write_clr_set(sd, 0x02, 0xf0, 0xf0);
break;
}
@@ -980,10 +1156,10 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd)
* input format (CE/IT) in automatic mode */
if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) {
/* RGB limited range (16-235) */
- io_write_and_or(sd, 0x02, 0x0f, 0x00);
+ io_write_clr_set(sd, 0x02, 0xf0, 0x00);
} else {
/* RGB full range (0-255) */
- io_write_and_or(sd, 0x02, 0x0f, 0x10);
+ io_write_clr_set(sd, 0x02, 0xf0, 0x10);
if (is_digital_input(sd) && rgb_output) {
adv7604_set_offset(sd, false, 0x40, 0x40, 0x40);
@@ -994,25 +1170,25 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd)
}
break;
case V4L2_DV_RGB_RANGE_LIMITED:
- if (state->selected_input == ADV7604_INPUT_VGA_COMP) {
+ if (state->selected_input == ADV7604_PAD_VGA_COMP) {
/* YCrCb limited range (16-235) */
- io_write_and_or(sd, 0x02, 0x0f, 0x20);
+ io_write_clr_set(sd, 0x02, 0xf0, 0x20);
break;
}
/* RGB limited range (16-235) */
- io_write_and_or(sd, 0x02, 0x0f, 0x00);
+ io_write_clr_set(sd, 0x02, 0xf0, 0x00);
break;
case V4L2_DV_RGB_RANGE_FULL:
- if (state->selected_input == ADV7604_INPUT_VGA_COMP) {
+ if (state->selected_input == ADV7604_PAD_VGA_COMP) {
/* YCrCb full range (0-255) */
- io_write_and_or(sd, 0x02, 0x0f, 0x60);
+ io_write_clr_set(sd, 0x02, 0xf0, 0x60);
break;
}
/* RGB full range (0-255) */
- io_write_and_or(sd, 0x02, 0x0f, 0x10);
+ io_write_clr_set(sd, 0x02, 0xf0, 0x10);
if (is_analog_input(sd) || hdmi_signal)
break;
@@ -1030,7 +1206,9 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd)
static int adv7604_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct v4l2_subdev *sd = to_sd(ctrl);
+ struct v4l2_subdev *sd =
+ &container_of(ctrl->handler, struct adv7604_state, hdl)->sd;
+
struct adv7604_state *state = to_state(sd);
switch (ctrl->id) {
@@ -1051,6 +1229,8 @@ static int adv7604_s_ctrl(struct v4l2_ctrl *ctrl)
set_rgb_quantization_range(sd);
return 0;
case V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE:
+ if (!adv7604_has_afe(state))
+ return -EINVAL;
/* Set the analog sampling phase. This is needed to find the
best sampling phase for analog video: an application or
driver has to try a number of phases and analyze the picture
@@ -1060,7 +1240,7 @@ static int adv7604_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_ADV_RX_FREE_RUN_COLOR_MANUAL:
/* Use the default blue color for free running mode,
or supply your own. */
- cp_write_and_or(sd, 0xbf, ~0x04, (ctrl->val << 2));
+ cp_write_clr_set(sd, 0xbf, 0x04, ctrl->val << 2);
return 0;
case V4L2_CID_ADV_RX_FREE_RUN_COLOR:
cp_write(sd, 0xc0, (ctrl->val & 0xff0000) >> 16);
@@ -1088,7 +1268,10 @@ static inline bool no_signal_tmds(struct v4l2_subdev *sd)
static inline bool no_lock_tmds(struct v4l2_subdev *sd)
{
- return (io_read(sd, 0x6a) & 0xe0) != 0xe0;
+ struct adv7604_state *state = to_state(sd);
+ const struct adv7604_chip_info *info = state->info;
+
+ return (io_read(sd, 0x6a) & info->tdms_lock_mask) != info->tdms_lock_mask;
}
static inline bool is_hdmi(struct v4l2_subdev *sd)
@@ -1098,6 +1281,15 @@ static inline bool is_hdmi(struct v4l2_subdev *sd)
static inline bool no_lock_sspd(struct v4l2_subdev *sd)
{
+ struct adv7604_state *state = to_state(sd);
+
+ /*
+ * Chips without a AFE don't expose registers for the SSPD, so just assume
+ * that we have a lock.
+ */
+ if (adv7604_has_afe(state))
+ return false;
+
/* TODO channel 2 */
return ((cp_read(sd, 0xb5) & 0xd0) != 0xd0);
}
@@ -1127,6 +1319,11 @@ static inline bool no_signal(struct v4l2_subdev *sd)
static inline bool no_lock_cp(struct v4l2_subdev *sd)
{
+ struct adv7604_state *state = to_state(sd);
+
+ if (!adv7604_has_afe(state))
+ return false;
+
/* CP has detected a non standard number of lines on the incoming
video compared to what it is configured to receive by s_dv_timings */
return io_read(sd, 0x12) & 0x01;
@@ -1195,28 +1392,40 @@ static int stdi2dv_timings(struct v4l2_subdev *sd,
return -1;
}
+
static int read_stdi(struct v4l2_subdev *sd, struct stdi_readback *stdi)
{
+ struct adv7604_state *state = to_state(sd);
+ const struct adv7604_chip_info *info = state->info;
+ u8 polarity;
+
if (no_lock_stdi(sd) || no_lock_sspd(sd)) {
v4l2_dbg(2, debug, sd, "%s: STDI and/or SSPD not locked\n", __func__);
return -1;
}
/* read STDI */
- stdi->bl = ((cp_read(sd, 0xb1) & 0x3f) << 8) | cp_read(sd, 0xb2);
- stdi->lcf = ((cp_read(sd, 0xb3) & 0x7) << 8) | cp_read(sd, 0xb4);
+ stdi->bl = cp_read16(sd, 0xb1, 0x3fff);
+ stdi->lcf = cp_read16(sd, info->lcf_reg, 0x7ff);
stdi->lcvs = cp_read(sd, 0xb3) >> 3;
stdi->interlaced = io_read(sd, 0x12) & 0x10;
- /* read SSPD */
- if ((cp_read(sd, 0xb5) & 0x03) == 0x01) {
- stdi->hs_pol = ((cp_read(sd, 0xb5) & 0x10) ?
- ((cp_read(sd, 0xb5) & 0x08) ? '+' : '-') : 'x');
- stdi->vs_pol = ((cp_read(sd, 0xb5) & 0x40) ?
- ((cp_read(sd, 0xb5) & 0x20) ? '+' : '-') : 'x');
+ if (adv7604_has_afe(state)) {
+ /* read SSPD */
+ polarity = cp_read(sd, 0xb5);
+ if ((polarity & 0x03) == 0x01) {
+ stdi->hs_pol = polarity & 0x10
+ ? (polarity & 0x08 ? '+' : '-') : 'x';
+ stdi->vs_pol = polarity & 0x40
+ ? (polarity & 0x20 ? '+' : '-') : 'x';
+ } else {
+ stdi->hs_pol = 'x';
+ stdi->vs_pol = 'x';
+ }
} else {
- stdi->hs_pol = 'x';
- stdi->vs_pol = 'x';
+ polarity = hdmi_read(sd, 0x05);
+ stdi->hs_pol = polarity & 0x20 ? '+' : '-';
+ stdi->vs_pol = polarity & 0x10 ? '+' : '-';
}
if (no_lock_stdi(sd) || no_lock_sspd(sd)) {
@@ -1243,8 +1452,14 @@ static int read_stdi(struct v4l2_subdev *sd, struct stdi_readback *stdi)
static int adv7604_enum_dv_timings(struct v4l2_subdev *sd,
struct v4l2_enum_dv_timings *timings)
{
+ struct adv7604_state *state = to_state(sd);
+
if (timings->index >= ARRAY_SIZE(adv7604_timings) - 1)
return -EINVAL;
+
+ if (timings->pad >= state->source_pad)
+ return -EINVAL;
+
memset(timings->reserved, 0, sizeof(timings->reserved));
timings->timings = adv7604_timings[timings->index];
return 0;
@@ -1253,14 +1468,30 @@ static int adv7604_enum_dv_timings(struct v4l2_subdev *sd,
static int adv7604_dv_timings_cap(struct v4l2_subdev *sd,
struct v4l2_dv_timings_cap *cap)
{
+ struct adv7604_state *state = to_state(sd);
+
+ if (cap->pad >= state->source_pad)
+ return -EINVAL;
+
cap->type = V4L2_DV_BT_656_1120;
cap->bt.max_width = 1920;
cap->bt.max_height = 1200;
cap->bt.min_pixelclock = 25000000;
- if (is_digital_input(sd))
+
+ switch (cap->pad) {
+ case ADV7604_PAD_HDMI_PORT_A:
+ case ADV7604_PAD_HDMI_PORT_B:
+ case ADV7604_PAD_HDMI_PORT_C:
+ case ADV7604_PAD_HDMI_PORT_D:
cap->bt.max_pixelclock = 225000000;
- else
+ break;
+ case ADV7604_PAD_VGA_RGB:
+ case ADV7604_PAD_VGA_COMP:
+ default:
cap->bt.max_pixelclock = 170000000;
+ break;
+ }
+
cap->bt.standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT;
cap->bt.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE |
@@ -1284,10 +1515,43 @@ static void adv7604_fill_optional_dv_timings_fields(struct v4l2_subdev *sd,
}
}
+static unsigned int adv7604_read_hdmi_pixelclock(struct v4l2_subdev *sd)
+{
+ unsigned int freq;
+ int a, b;
+
+ a = hdmi_read(sd, 0x06);
+ b = hdmi_read(sd, 0x3b);
+ if (a < 0 || b < 0)
+ return 0;
+ freq = a * 1000000 + ((b & 0x30) >> 4) * 250000;
+
+ if (is_hdmi(sd)) {
+ /* adjust for deep color mode */
+ unsigned bits_per_channel = ((hdmi_read(sd, 0x0b) & 0x60) >> 4) + 8;
+
+ freq = freq * 8 / bits_per_channel;
+ }
+
+ return freq;
+}
+
+static unsigned int adv7611_read_hdmi_pixelclock(struct v4l2_subdev *sd)
+{
+ int a, b;
+
+ a = hdmi_read(sd, 0x51);
+ b = hdmi_read(sd, 0x52);
+ if (a < 0 || b < 0)
+ return 0;
+ return ((a << 1) | (b >> 7)) * 1000000 + (b & 0x7f) * 1000000 / 128;
+}
+
static int adv7604_query_dv_timings(struct v4l2_subdev *sd,
struct v4l2_dv_timings *timings)
{
struct adv7604_state *state = to_state(sd);
+ const struct adv7604_chip_info *info = state->info;
struct v4l2_bt_timings *bt = &timings->bt;
struct stdi_readback stdi;
@@ -1311,44 +1575,25 @@ static int adv7604_query_dv_timings(struct v4l2_subdev *sd,
V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE;
if (is_digital_input(sd)) {
- uint32_t freq;
-
timings->type = V4L2_DV_BT_656_1120;
- bt->width = (hdmi_read(sd, 0x07) & 0x0f) * 256 + hdmi_read(sd, 0x08);
- bt->height = (hdmi_read(sd, 0x09) & 0x0f) * 256 + hdmi_read(sd, 0x0a);
- freq = (hdmi_read(sd, 0x06) * 1000000) +
- ((hdmi_read(sd, 0x3b) & 0x30) >> 4) * 250000;
- if (is_hdmi(sd)) {
- /* adjust for deep color mode */
- unsigned bits_per_channel = ((hdmi_read(sd, 0x0b) & 0x60) >> 4) + 8;
-
- freq = freq * 8 / bits_per_channel;
- }
- bt->pixelclock = freq;
- bt->hfrontporch = (hdmi_read(sd, 0x20) & 0x03) * 256 +
- hdmi_read(sd, 0x21);
- bt->hsync = (hdmi_read(sd, 0x22) & 0x03) * 256 +
- hdmi_read(sd, 0x23);
- bt->hbackporch = (hdmi_read(sd, 0x24) & 0x03) * 256 +
- hdmi_read(sd, 0x25);
- bt->vfrontporch = ((hdmi_read(sd, 0x2a) & 0x1f) * 256 +
- hdmi_read(sd, 0x2b)) / 2;
- bt->vsync = ((hdmi_read(sd, 0x2e) & 0x1f) * 256 +
- hdmi_read(sd, 0x2f)) / 2;
- bt->vbackporch = ((hdmi_read(sd, 0x32) & 0x1f) * 256 +
- hdmi_read(sd, 0x33)) / 2;
+ /* FIXME: All masks are incorrect for ADV7611 */
+ bt->width = hdmi_read16(sd, 0x07, 0xfff);
+ bt->height = hdmi_read16(sd, 0x09, 0xfff);
+ bt->pixelclock = info->read_hdmi_pixelclock(sd);
+ bt->hfrontporch = hdmi_read16(sd, 0x20, 0x3ff);
+ bt->hsync = hdmi_read16(sd, 0x22, 0x3ff);
+ bt->hbackporch = hdmi_read16(sd, 0x24, 0x3ff);
+ bt->vfrontporch = hdmi_read16(sd, 0x2a, 0x1fff) / 2;
+ bt->vsync = hdmi_read16(sd, 0x2e, 0x1fff) / 2;
+ bt->vbackporch = hdmi_read16(sd, 0x32, 0x1fff) / 2;
bt->polarities = ((hdmi_read(sd, 0x05) & 0x10) ? V4L2_DV_VSYNC_POS_POL : 0) |
((hdmi_read(sd, 0x05) & 0x20) ? V4L2_DV_HSYNC_POS_POL : 0);
if (bt->interlaced == V4L2_DV_INTERLACED) {
- bt->height += (hdmi_read(sd, 0x0b) & 0x0f) * 256 +
- hdmi_read(sd, 0x0c);
- bt->il_vfrontporch = ((hdmi_read(sd, 0x2c) & 0x1f) * 256 +
- hdmi_read(sd, 0x2d)) / 2;
- bt->il_vsync = ((hdmi_read(sd, 0x30) & 0x1f) * 256 +
- hdmi_read(sd, 0x31)) / 2;
- bt->vbackporch = ((hdmi_read(sd, 0x34) & 0x1f) * 256 +
- hdmi_read(sd, 0x35)) / 2;
+ bt->height += hdmi_read16(sd, 0x0b, 0xfff);
+ bt->il_vfrontporch = hdmi_read16(sd, 0x2c, 0x1fff) / 2;
+ bt->il_vsync = hdmi_read16(sd, 0x30, 0x1fff) / 2;
+ bt->vbackporch = hdmi_read16(sd, 0x34, 0x1fff) / 2;
}
adv7604_fill_optional_dv_timings_fields(sd, timings);
} else {
@@ -1378,11 +1623,11 @@ static int adv7604_query_dv_timings(struct v4l2_subdev *sd,
v4l2_dbg(1, debug, sd, "%s: restart STDI\n", __func__);
/* TODO restart STDI for Sync Channel 2 */
/* enter one-shot mode */
- cp_write_and_or(sd, 0x86, 0xf9, 0x00);
+ cp_write_clr_set(sd, 0x86, 0x06, 0x00);
/* trigger STDI restart */
- cp_write_and_or(sd, 0x86, 0xf9, 0x04);
+ cp_write_clr_set(sd, 0x86, 0x06, 0x04);
/* reset to continuous mode */
- cp_write_and_or(sd, 0x86, 0xf9, 0x02);
+ cp_write_clr_set(sd, 0x86, 0x06, 0x02);
state->restart_stdi_once = false;
return -ENOLINK;
}
@@ -1441,7 +1686,7 @@ static int adv7604_s_dv_timings(struct v4l2_subdev *sd,
state->timings = *timings;
- cp_write(sd, 0x91, bt->interlaced ? 0x50 : 0x10);
+ cp_write_clr_set(sd, 0x91, 0x40, bt->interlaced ? 0x40 : 0x00);
/* Use prim_mode and vid_std when available */
err = configure_predefined_video_timings(sd, timings);
@@ -1468,6 +1713,16 @@ static int adv7604_g_dv_timings(struct v4l2_subdev *sd,
return 0;
}
+static void adv7604_set_termination(struct v4l2_subdev *sd, bool enable)
+{
+ hdmi_write(sd, 0x01, enable ? 0x00 : 0x78);
+}
+
+static void adv7611_set_termination(struct v4l2_subdev *sd, bool enable)
+{
+ hdmi_write(sd, 0x83, enable ? 0xfe : 0xff);
+}
+
static void enable_input(struct v4l2_subdev *sd)
{
struct adv7604_state *state = to_state(sd);
@@ -1475,10 +1730,10 @@ static void enable_input(struct v4l2_subdev *sd)
if (is_analog_input(sd)) {
io_write(sd, 0x15, 0xb0); /* Disable Tristate of Pins (no audio) */
} else if (is_digital_input(sd)) {
- hdmi_write_and_or(sd, 0x00, 0xfc, state->selected_input);
- hdmi_write(sd, 0x01, 0x00); /* Enable HDMI clock terminators */
+ hdmi_write_clr_set(sd, 0x00, 0x03, state->selected_input);
+ state->info->set_termination(sd, true);
io_write(sd, 0x15, 0xa0); /* Disable Tristate of Pins */
- hdmi_write_and_or(sd, 0x1a, 0xef, 0x00); /* Unmute audio */
+ hdmi_write_clr_set(sd, 0x1a, 0x10, 0x00); /* Unmute audio */
} else {
v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n",
__func__, state->selected_input);
@@ -1487,67 +1742,36 @@ static void enable_input(struct v4l2_subdev *sd)
static void disable_input(struct v4l2_subdev *sd)
{
- hdmi_write_and_or(sd, 0x1a, 0xef, 0x10); /* Mute audio */
+ struct adv7604_state *state = to_state(sd);
+
+ hdmi_write_clr_set(sd, 0x1a, 0x10, 0x10); /* Mute audio */
msleep(16); /* 512 samples with >= 32 kHz sample rate [REF_03, c. 7.16.10] */
io_write(sd, 0x15, 0xbe); /* Tristate all outputs from video core */
- hdmi_write(sd, 0x01, 0x78); /* Disable HDMI clock terminators */
+ state->info->set_termination(sd, false);
}
static void select_input(struct v4l2_subdev *sd)
{
struct adv7604_state *state = to_state(sd);
+ const struct adv7604_chip_info *info = state->info;
if (is_analog_input(sd)) {
- /* reset ADI recommended settings for HDMI: */
- /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */
- hdmi_write(sd, 0x0d, 0x04); /* HDMI filter optimization */
- hdmi_write(sd, 0x3d, 0x00); /* DDC bus active pull-up control */
- hdmi_write(sd, 0x3e, 0x74); /* TMDS PLL optimization */
- hdmi_write(sd, 0x4e, 0x3b); /* TMDS PLL optimization */
- hdmi_write(sd, 0x57, 0x74); /* TMDS PLL optimization */
- hdmi_write(sd, 0x58, 0x63); /* TMDS PLL optimization */
- hdmi_write(sd, 0x8d, 0x18); /* equaliser */
- hdmi_write(sd, 0x8e, 0x34); /* equaliser */
- hdmi_write(sd, 0x93, 0x88); /* equaliser */
- hdmi_write(sd, 0x94, 0x2e); /* equaliser */
- hdmi_write(sd, 0x96, 0x00); /* enable automatic EQ changing */
+ adv7604_write_reg_seq(sd, info->recommended_settings[0]);
afe_write(sd, 0x00, 0x08); /* power up ADC */
afe_write(sd, 0x01, 0x06); /* power up Analog Front End */
afe_write(sd, 0xc8, 0x00); /* phase control */
-
- /* set ADI recommended settings for digitizer */
- /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */
- afe_write(sd, 0x12, 0x7b); /* ADC noise shaping filter controls */
- afe_write(sd, 0x0c, 0x1f); /* CP core gain controls */
- cp_write(sd, 0x3e, 0x04); /* CP core pre-gain control */
- cp_write(sd, 0xc3, 0x39); /* CP coast control. Graphics mode */
- cp_write(sd, 0x40, 0x5c); /* CP core pre-gain control. Graphics mode */
} else if (is_digital_input(sd)) {
hdmi_write(sd, 0x00, state->selected_input & 0x03);
- /* set ADI recommended settings for HDMI: */
- /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */
- hdmi_write(sd, 0x0d, 0x84); /* HDMI filter optimization */
- hdmi_write(sd, 0x3d, 0x10); /* DDC bus active pull-up control */
- hdmi_write(sd, 0x3e, 0x39); /* TMDS PLL optimization */
- hdmi_write(sd, 0x4e, 0x3b); /* TMDS PLL optimization */
- hdmi_write(sd, 0x57, 0xb6); /* TMDS PLL optimization */
- hdmi_write(sd, 0x58, 0x03); /* TMDS PLL optimization */
- hdmi_write(sd, 0x8d, 0x18); /* equaliser */
- hdmi_write(sd, 0x8e, 0x34); /* equaliser */
- hdmi_write(sd, 0x93, 0x8b); /* equaliser */
- hdmi_write(sd, 0x94, 0x2d); /* equaliser */
- hdmi_write(sd, 0x96, 0x01); /* enable automatic EQ changing */
-
- afe_write(sd, 0x00, 0xff); /* power down ADC */
- afe_write(sd, 0x01, 0xfe); /* power down Analog Front End */
- afe_write(sd, 0xc8, 0x40); /* phase control */
-
- /* reset ADI recommended settings for digitizer */
- /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */
- afe_write(sd, 0x12, 0xfb); /* ADC noise shaping filter controls */
- afe_write(sd, 0x0c, 0x0d); /* CP core gain controls */
+ adv7604_write_reg_seq(sd, info->recommended_settings[1]);
+
+ if (adv7604_has_afe(state)) {
+ afe_write(sd, 0x00, 0xff); /* power down ADC */
+ afe_write(sd, 0x01, 0xfe); /* power down Analog Front End */
+ afe_write(sd, 0xc8, 0x40); /* phase control */
+ }
+
cp_write(sd, 0x3e, 0x00); /* CP core pre-gain control */
cp_write(sd, 0xc3, 0x39); /* CP coast control. Graphics mode */
cp_write(sd, 0x40, 0x80); /* CP core pre-gain control. Graphics mode */
@@ -1568,6 +1792,9 @@ static int adv7604_s_routing(struct v4l2_subdev *sd,
if (input == state->selected_input)
return 0;
+ if (input > state->info->max_port)
+ return -EINVAL;
+
state->selected_input = input;
disable_input(sd);
@@ -1579,34 +1806,139 @@ static int adv7604_s_routing(struct v4l2_subdev *sd,
return 0;
}
-static int adv7604_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
+static int adv7604_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
{
- if (index)
+ struct adv7604_state *state = to_state(sd);
+
+ if (code->index >= state->info->nformats)
return -EINVAL;
- /* Good enough for now */
- *code = V4L2_MBUS_FMT_FIXED;
+
+ code->code = state->info->formats[code->index].code;
+
return 0;
}
-static int adv7604_g_mbus_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *fmt)
+static void adv7604_fill_format(struct adv7604_state *state,
+ struct v4l2_mbus_framefmt *format)
{
- struct adv7604_state *state = to_state(sd);
+ memset(format, 0, sizeof(*format));
+
+ format->width = state->timings.bt.width;
+ format->height = state->timings.bt.height;
+ format->field = V4L2_FIELD_NONE;
- fmt->width = state->timings.bt.width;
- fmt->height = state->timings.bt.height;
- fmt->code = V4L2_MBUS_FMT_FIXED;
- fmt->field = V4L2_FIELD_NONE;
- if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) {
- fmt->colorspace = (state->timings.bt.height <= 576) ?
+ if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861)
+ format->colorspace = (state->timings.bt.height <= 576) ?
V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709;
+}
+
+/*
+ * Compute the op_ch_sel value required to obtain on the bus the component order
+ * corresponding to the selected format taking into account bus reordering
+ * applied by the board at the output of the device.
+ *
+ * The following table gives the op_ch_value from the format component order
+ * (expressed as op_ch_sel value in column) and the bus reordering (expressed as
+ * adv7604_bus_order value in row).
+ *
+ * | GBR(0) GRB(1) BGR(2) RGB(3) BRG(4) RBG(5)
+ * ----------+-------------------------------------------------
+ * RGB (NOP) | GBR GRB BGR RGB BRG RBG
+ * GRB (1-2) | BGR RGB GBR GRB RBG BRG
+ * RBG (2-3) | GRB GBR BRG RBG BGR RGB
+ * BGR (1-3) | RBG BRG RGB BGR GRB GBR
+ * BRG (ROR) | BRG RBG GRB GBR RGB BGR
+ * GBR (ROL) | RGB BGR RBG BRG GBR GRB
+ */
+static unsigned int adv7604_op_ch_sel(struct adv7604_state *state)
+{
+#define _SEL(a,b,c,d,e,f) { \
+ ADV7604_OP_CH_SEL_##a, ADV7604_OP_CH_SEL_##b, ADV7604_OP_CH_SEL_##c, \
+ ADV7604_OP_CH_SEL_##d, ADV7604_OP_CH_SEL_##e, ADV7604_OP_CH_SEL_##f }
+#define _BUS(x) [ADV7604_BUS_ORDER_##x]
+
+ static const unsigned int op_ch_sel[6][6] = {
+ _BUS(RGB) /* NOP */ = _SEL(GBR, GRB, BGR, RGB, BRG, RBG),
+ _BUS(GRB) /* 1-2 */ = _SEL(BGR, RGB, GBR, GRB, RBG, BRG),
+ _BUS(RBG) /* 2-3 */ = _SEL(GRB, GBR, BRG, RBG, BGR, RGB),
+ _BUS(BGR) /* 1-3 */ = _SEL(RBG, BRG, RGB, BGR, GRB, GBR),
+ _BUS(BRG) /* ROR */ = _SEL(BRG, RBG, GRB, GBR, RGB, BGR),
+ _BUS(GBR) /* ROL */ = _SEL(RGB, BGR, RBG, BRG, GBR, GRB),
+ };
+
+ return op_ch_sel[state->pdata.bus_order][state->format->op_ch_sel >> 5];
+}
+
+static void adv7604_setup_format(struct adv7604_state *state)
+{
+ struct v4l2_subdev *sd = &state->sd;
+
+ io_write_clr_set(sd, 0x02, 0x02,
+ state->format->rgb_out ? ADV7604_RGB_OUT : 0);
+ io_write(sd, 0x03, state->format->op_format_sel |
+ state->pdata.op_format_mode_sel);
+ io_write_clr_set(sd, 0x04, 0xe0, adv7604_op_ch_sel(state));
+ io_write_clr_set(sd, 0x05, 0x01,
+ state->format->swap_cb_cr ? ADV7604_OP_SWAP_CB_CR : 0);
+}
+
+static int adv7604_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *format)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ if (format->pad != state->source_pad)
+ return -EINVAL;
+
+ adv7604_fill_format(state, &format->format);
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_mbus_framefmt *fmt;
+
+ fmt = v4l2_subdev_get_try_format(fh, format->pad);
+ format->format.code = fmt->code;
+ } else {
+ format->format.code = state->format->code;
}
+
+ return 0;
+}
+
+static int adv7604_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *format)
+{
+ struct adv7604_state *state = to_state(sd);
+ const struct adv7604_format_info *info;
+
+ if (format->pad != state->source_pad)
+ return -EINVAL;
+
+ info = adv7604_format_info(state, format->format.code);
+ if (info == NULL)
+ info = adv7604_format_info(state, V4L2_MBUS_FMT_YUYV8_2X8);
+
+ adv7604_fill_format(state, &format->format);
+ format->format.code = info->code;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_mbus_framefmt *fmt;
+
+ fmt = v4l2_subdev_get_try_format(fh, format->pad);
+ fmt->code = format->format.code;
+ } else {
+ state->format = info;
+ adv7604_setup_format(state);
+ }
+
return 0;
}
static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
{
+ struct adv7604_state *state = to_state(sd);
+ const struct adv7604_chip_info *info = state->info;
const u8 irq_reg_0x43 = io_read(sd, 0x43);
const u8 irq_reg_0x6b = io_read(sd, 0x6b);
const u8 irq_reg_0x70 = io_read(sd, 0x70);
@@ -1625,7 +1957,9 @@ static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
/* format change */
fmt_change = irq_reg_0x43 & 0x98;
- fmt_change_digital = is_digital_input(sd) ? (irq_reg_0x6b & 0xc0) : 0;
+ fmt_change_digital = is_digital_input(sd)
+ ? irq_reg_0x6b & info->fmt_change_digital_mask
+ : 0;
if (fmt_change || fmt_change_digital) {
v4l2_dbg(1, debug, sd,
@@ -1647,7 +1981,7 @@ static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
}
/* tx 5v detect */
- tx_5v = io_read(sd, 0x70) & 0x1e;
+ tx_5v = io_read(sd, 0x70) & info->cable_det_mask;
if (tx_5v) {
v4l2_dbg(1, debug, sd, "%s: tx_5v: 0x%x\n", __func__, tx_5v);
io_write(sd, 0x71, tx_5v);
@@ -1658,12 +1992,12 @@ static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
return 0;
}
-static int adv7604_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid)
+static int adv7604_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
{
struct adv7604_state *state = to_state(sd);
u8 *data = NULL;
- if (edid->pad > ADV7604_EDID_PORT_D)
+ if (edid->pad > ADV7604_PAD_HDMI_PORT_D)
return -EINVAL;
if (edid->blocks == 0)
return -EINVAL;
@@ -1673,17 +2007,15 @@ static int adv7604_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edi
return -EINVAL;
if (edid->start_block == 1)
edid->blocks = 1;
- if (!edid->edid)
- return -EINVAL;
if (edid->blocks > state->edid.blocks)
edid->blocks = state->edid.blocks;
switch (edid->pad) {
- case ADV7604_EDID_PORT_A:
- case ADV7604_EDID_PORT_B:
- case ADV7604_EDID_PORT_C:
- case ADV7604_EDID_PORT_D:
+ case ADV7604_PAD_HDMI_PORT_A:
+ case ADV7604_PAD_HDMI_PORT_B:
+ case ADV7604_PAD_HDMI_PORT_C:
+ case ADV7604_PAD_HDMI_PORT_D:
if (state->edid.present & (1 << edid->pad))
data = state->edid.edid;
break;
@@ -1728,23 +2060,23 @@ static int get_edid_spa_location(const u8 *edid)
return -1;
}
-static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid)
+static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
{
struct adv7604_state *state = to_state(sd);
+ const struct adv7604_chip_info *info = state->info;
int spa_loc;
- int tmp = 0;
int err;
int i;
- if (edid->pad > ADV7604_EDID_PORT_D)
+ if (edid->pad > ADV7604_PAD_HDMI_PORT_D)
return -EINVAL;
if (edid->start_block != 0)
return -EINVAL;
if (edid->blocks == 0) {
/* Disable hotplug and I2C access to EDID RAM from DDC port */
state->edid.present &= ~(1 << edid->pad);
- v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)&state->edid.present);
- rep_write_and_or(sd, 0x77, 0xf0, state->edid.present);
+ adv7604_set_hpd(state, state->edid.present);
+ rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, state->edid.present);
/* Fall back to a 16:9 aspect ratio */
state->aspect_ratio.numerator = 16;
@@ -1761,43 +2093,47 @@ static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edi
edid->blocks = 2;
return -E2BIG;
}
- if (!edid->edid)
- return -EINVAL;
v4l2_dbg(2, debug, sd, "%s: write EDID pad %d, edid.present = 0x%x\n",
__func__, edid->pad, state->edid.present);
/* Disable hotplug and I2C access to EDID RAM from DDC port */
cancel_delayed_work_sync(&state->delayed_work_enable_hotplug);
- v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)&tmp);
- rep_write_and_or(sd, 0x77, 0xf0, 0x00);
+ adv7604_set_hpd(state, 0);
+ rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, 0x00);
spa_loc = get_edid_spa_location(edid->edid);
if (spa_loc < 0)
spa_loc = 0xc0; /* Default value [REF_02, p. 116] */
switch (edid->pad) {
- case ADV7604_EDID_PORT_A:
+ case ADV7604_PAD_HDMI_PORT_A:
state->spa_port_a[0] = edid->edid[spa_loc];
state->spa_port_a[1] = edid->edid[spa_loc + 1];
break;
- case ADV7604_EDID_PORT_B:
+ case ADV7604_PAD_HDMI_PORT_B:
rep_write(sd, 0x70, edid->edid[spa_loc]);
rep_write(sd, 0x71, edid->edid[spa_loc + 1]);
break;
- case ADV7604_EDID_PORT_C:
+ case ADV7604_PAD_HDMI_PORT_C:
rep_write(sd, 0x72, edid->edid[spa_loc]);
rep_write(sd, 0x73, edid->edid[spa_loc + 1]);
break;
- case ADV7604_EDID_PORT_D:
+ case ADV7604_PAD_HDMI_PORT_D:
rep_write(sd, 0x74, edid->edid[spa_loc]);
rep_write(sd, 0x75, edid->edid[spa_loc + 1]);
break;
default:
return -EINVAL;
}
- rep_write(sd, 0x76, spa_loc & 0xff);
- rep_write_and_or(sd, 0x77, 0xbf, (spa_loc >> 2) & 0x40);
+
+ if (info->type == ADV7604) {
+ rep_write(sd, 0x76, spa_loc & 0xff);
+ rep_write_clr_set(sd, 0x77, 0x40, (spa_loc & 0x100) >> 2);
+ } else {
+ /* FIXME: Where is the SPA location LSB register ? */
+ rep_write_clr_set(sd, 0x71, 0x01, (spa_loc & 0x100) >> 8);
+ }
edid->edid[spa_loc] = state->spa_port_a[0];
edid->edid[spa_loc + 1] = state->spa_port_a[1];
@@ -1816,10 +2152,10 @@ static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edi
/* adv7604 calculates the checksums and enables I2C access to internal
EDID RAM from DDC port. */
- rep_write_and_or(sd, 0x77, 0xf0, state->edid.present);
+ rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, state->edid.present);
for (i = 0; i < 1000; i++) {
- if (rep_read(sd, 0x7d) & state->edid.present)
+ if (rep_read(sd, info->edid_status_reg) & state->edid.present)
break;
mdelay(1);
}
@@ -1882,17 +2218,20 @@ static void print_avi_infoframe(struct v4l2_subdev *sd)
static int adv7604_log_status(struct v4l2_subdev *sd)
{
struct adv7604_state *state = to_state(sd);
+ const struct adv7604_chip_info *info = state->info;
struct v4l2_dv_timings timings;
struct stdi_readback stdi;
u8 reg_io_0x02 = io_read(sd, 0x02);
+ u8 edid_enabled;
+ u8 cable_det;
- char *csc_coeff_sel_rb[16] = {
+ static const char * const csc_coeff_sel_rb[16] = {
"bypassed", "YPbPr601 -> RGB", "reserved", "YPbPr709 -> RGB",
"reserved", "RGB -> YPbPr601", "reserved", "RGB -> YPbPr709",
"reserved", "YPbPr709 -> YPbPr601", "YPbPr601 -> YPbPr709",
"reserved", "reserved", "reserved", "reserved", "manual"
};
- char *input_color_space_txt[16] = {
+ static const char * const input_color_space_txt[16] = {
"RGB limited range (16-235)", "RGB full range (0-255)",
"YCbCr Bt.601 (16-235)", "YCbCr Bt.709 (16-235)",
"xvYCC Bt.601", "xvYCC Bt.709",
@@ -1900,12 +2239,12 @@ static int adv7604_log_status(struct v4l2_subdev *sd)
"invalid", "invalid", "invalid", "invalid", "invalid",
"invalid", "invalid", "automatic"
};
- char *rgb_quantization_range_txt[] = {
+ static const char * const rgb_quantization_range_txt[] = {
"Automatic",
"RGB limited range (16-235)",
"RGB full range (0-255)",
};
- char *deep_color_mode_txt[4] = {
+ static const char * const deep_color_mode_txt[4] = {
"8-bits per channel",
"10-bits per channel",
"12-bits per channel",
@@ -1914,20 +2253,22 @@ static int adv7604_log_status(struct v4l2_subdev *sd)
v4l2_info(sd, "-----Chip status-----\n");
v4l2_info(sd, "Chip power: %s\n", no_power(sd) ? "off" : "on");
+ edid_enabled = rep_read(sd, info->edid_status_reg);
v4l2_info(sd, "EDID enabled port A: %s, B: %s, C: %s, D: %s\n",
- ((rep_read(sd, 0x7d) & 0x01) ? "Yes" : "No"),
- ((rep_read(sd, 0x7d) & 0x02) ? "Yes" : "No"),
- ((rep_read(sd, 0x7d) & 0x04) ? "Yes" : "No"),
- ((rep_read(sd, 0x7d) & 0x08) ? "Yes" : "No"));
+ ((edid_enabled & 0x01) ? "Yes" : "No"),
+ ((edid_enabled & 0x02) ? "Yes" : "No"),
+ ((edid_enabled & 0x04) ? "Yes" : "No"),
+ ((edid_enabled & 0x08) ? "Yes" : "No"));
v4l2_info(sd, "CEC: %s\n", !!(cec_read(sd, 0x2a) & 0x01) ?
"enabled" : "disabled");
v4l2_info(sd, "-----Signal status-----\n");
+ cable_det = info->read_cable_det(sd);
v4l2_info(sd, "Cable detected (+5V power) port A: %s, B: %s, C: %s, D: %s\n",
- ((io_read(sd, 0x6f) & 0x10) ? "Yes" : "No"),
- ((io_read(sd, 0x6f) & 0x08) ? "Yes" : "No"),
- ((io_read(sd, 0x6f) & 0x04) ? "Yes" : "No"),
- ((io_read(sd, 0x6f) & 0x02) ? "Yes" : "No"));
+ ((cable_det & 0x01) ? "Yes" : "No"),
+ ((cable_det & 0x02) ? "Yes" : "No"),
+ ((cable_det & 0x04) ? "Yes" : "No"),
+ ((cable_det & 0x08) ? "Yes" : "No"));
v4l2_info(sd, "TMDS signal detected: %s\n",
no_signal_tmds(sd) ? "false" : "true");
v4l2_info(sd, "TMDS signal locked: %s\n",
@@ -2021,13 +2362,6 @@ static const struct v4l2_ctrl_ops adv7604_ctrl_ops = {
static const struct v4l2_subdev_core_ops adv7604_core_ops = {
.log_status = adv7604_log_status,
- .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
- .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
- .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
- .g_ctrl = v4l2_subdev_g_ctrl,
- .s_ctrl = v4l2_subdev_s_ctrl,
- .queryctrl = v4l2_subdev_queryctrl,
- .querymenu = v4l2_subdev_querymenu,
.interrupt_service_routine = adv7604_isr,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = adv7604_g_register,
@@ -2041,17 +2375,16 @@ static const struct v4l2_subdev_video_ops adv7604_video_ops = {
.s_dv_timings = adv7604_s_dv_timings,
.g_dv_timings = adv7604_g_dv_timings,
.query_dv_timings = adv7604_query_dv_timings,
- .enum_dv_timings = adv7604_enum_dv_timings,
- .dv_timings_cap = adv7604_dv_timings_cap,
- .enum_mbus_fmt = adv7604_enum_mbus_fmt,
- .g_mbus_fmt = adv7604_g_mbus_fmt,
- .try_mbus_fmt = adv7604_g_mbus_fmt,
- .s_mbus_fmt = adv7604_g_mbus_fmt,
};
static const struct v4l2_subdev_pad_ops adv7604_pad_ops = {
+ .enum_mbus_code = adv7604_enum_mbus_code,
+ .get_fmt = adv7604_get_format,
+ .set_fmt = adv7604_set_format,
.get_edid = adv7604_get_edid,
.set_edid = adv7604_set_edid,
+ .dv_timings_cap = adv7604_dv_timings_cap,
+ .enum_dv_timings = adv7604_enum_dv_timings,
};
static const struct v4l2_subdev_ops adv7604_ops = {
@@ -2100,6 +2433,7 @@ static const struct v4l2_ctrl_config adv7604_ctrl_free_run_color = {
static int adv7604_core_init(struct v4l2_subdev *sd)
{
struct adv7604_state *state = to_state(sd);
+ const struct adv7604_chip_info *info = state->info;
struct adv7604_platform_data *pdata = &state->pdata;
hdmi_write(sd, 0x48,
@@ -2108,28 +2442,33 @@ static int adv7604_core_init(struct v4l2_subdev *sd)
disable_input(sd);
+ if (pdata->default_input >= 0 &&
+ pdata->default_input < state->source_pad) {
+ state->selected_input = pdata->default_input;
+ select_input(sd);
+ enable_input(sd);
+ }
+
/* power */
io_write(sd, 0x0c, 0x42); /* Power up part and power down VDP */
io_write(sd, 0x0b, 0x44); /* Power down ESDP block */
cp_write(sd, 0xcf, 0x01); /* Power down macrovision */
/* video format */
- io_write_and_or(sd, 0x02, 0xf0,
+ io_write_clr_set(sd, 0x02, 0x0f,
pdata->alt_gamma << 3 |
pdata->op_656_range << 2 |
- pdata->rgb_out << 1 |
pdata->alt_data_sat << 0);
- io_write(sd, 0x03, pdata->op_format_sel);
- io_write_and_or(sd, 0x04, 0x1f, pdata->op_ch_sel << 5);
- io_write_and_or(sd, 0x05, 0xf0, pdata->blank_data << 3 |
- pdata->insert_av_codes << 2 |
- pdata->replicate_av_codes << 1 |
- pdata->invert_cbcr << 0);
+ io_write_clr_set(sd, 0x05, 0x0e, pdata->blank_data << 3 |
+ pdata->insert_av_codes << 2 |
+ pdata->replicate_av_codes << 1);
+ adv7604_setup_format(state);
cp_write(sd, 0x69, 0x30); /* Enable CP CSC */
/* VS, HS polarities */
- io_write(sd, 0x06, 0xa0 | pdata->inv_vs_pol << 2 | pdata->inv_hs_pol << 1);
+ io_write(sd, 0x06, 0xa0 | pdata->inv_vs_pol << 2 |
+ pdata->inv_hs_pol << 1 | pdata->inv_llc_pol);
/* Adjust drive strength */
io_write(sd, 0x14, 0x40 | pdata->dr_str_data << 4 |
@@ -2146,52 +2485,46 @@ static int adv7604_core_init(struct v4l2_subdev *sd)
for digital formats */
/* HDMI audio */
- hdmi_write_and_or(sd, 0x15, 0xfc, 0x03); /* Mute on FIFO over-/underflow [REF_01, c. 1.2.18] */
- hdmi_write_and_or(sd, 0x1a, 0xf1, 0x08); /* Wait 1 s before unmute */
- hdmi_write_and_or(sd, 0x68, 0xf9, 0x06); /* FIFO reset on over-/underflow [REF_01, c. 1.2.19] */
+ hdmi_write_clr_set(sd, 0x15, 0x03, 0x03); /* Mute on FIFO over-/underflow [REF_01, c. 1.2.18] */
+ hdmi_write_clr_set(sd, 0x1a, 0x0e, 0x08); /* Wait 1 s before unmute */
+ hdmi_write_clr_set(sd, 0x68, 0x06, 0x06); /* FIFO reset on over-/underflow [REF_01, c. 1.2.19] */
/* TODO from platform data */
afe_write(sd, 0xb5, 0x01); /* Setting MCLK to 256Fs */
- afe_write(sd, 0x02, pdata->ain_sel); /* Select analog input muxing mode */
- io_write_and_or(sd, 0x30, ~(1 << 4), pdata->output_bus_lsb_to_msb << 4);
+ if (adv7604_has_afe(state)) {
+ afe_write(sd, 0x02, pdata->ain_sel); /* Select analog input muxing mode */
+ io_write_clr_set(sd, 0x30, 1 << 4, pdata->output_bus_lsb_to_msb << 4);
+ }
/* interrupts */
- io_write(sd, 0x40, 0xc2); /* Configure INT1 */
- io_write(sd, 0x41, 0xd7); /* STDI irq for any change, disable INT2 */
+ io_write(sd, 0x40, 0xc0 | pdata->int1_config); /* Configure INT1 */
io_write(sd, 0x46, 0x98); /* Enable SSPD, STDI and CP unlocked interrupts */
- io_write(sd, 0x6e, 0xc1); /* Enable V_LOCKED, DE_REGEN_LCK, HDMI_MODE interrupts */
- io_write(sd, 0x73, 0x1e); /* Enable CABLE_DET_A_ST (+5v) interrupts */
+ io_write(sd, 0x6e, info->fmt_change_digital_mask); /* Enable V_LOCKED and DE_REGEN_LCK interrupts */
+ io_write(sd, 0x73, info->cable_det_mask); /* Enable cable detection (+5v) interrupts */
+ info->setup_irqs(sd);
return v4l2_ctrl_handler_setup(sd->ctrl_handler);
}
+static void adv7604_setup_irqs(struct v4l2_subdev *sd)
+{
+ io_write(sd, 0x41, 0xd7); /* STDI irq for any change, disable INT2 */
+}
+
+static void adv7611_setup_irqs(struct v4l2_subdev *sd)
+{
+ io_write(sd, 0x41, 0xd0); /* STDI irq for any change, disable INT2 */
+}
+
static void adv7604_unregister_clients(struct adv7604_state *state)
{
- if (state->i2c_avlink)
- i2c_unregister_device(state->i2c_avlink);
- if (state->i2c_cec)
- i2c_unregister_device(state->i2c_cec);
- if (state->i2c_infoframe)
- i2c_unregister_device(state->i2c_infoframe);
- if (state->i2c_esdp)
- i2c_unregister_device(state->i2c_esdp);
- if (state->i2c_dpp)
- i2c_unregister_device(state->i2c_dpp);
- if (state->i2c_afe)
- i2c_unregister_device(state->i2c_afe);
- if (state->i2c_repeater)
- i2c_unregister_device(state->i2c_repeater);
- if (state->i2c_edid)
- i2c_unregister_device(state->i2c_edid);
- if (state->i2c_hdmi)
- i2c_unregister_device(state->i2c_hdmi);
- if (state->i2c_test)
- i2c_unregister_device(state->i2c_test);
- if (state->i2c_cp)
- i2c_unregister_device(state->i2c_cp);
- if (state->i2c_vdp)
- i2c_unregister_device(state->i2c_vdp);
+ unsigned int i;
+
+ for (i = 1; i < ARRAY_SIZE(state->i2c_clients); ++i) {
+ if (state->i2c_clients[i])
+ i2c_unregister_device(state->i2c_clients[i]);
+ }
}
static struct i2c_client *adv7604_dummy_client(struct v4l2_subdev *sd,
@@ -2204,15 +2537,219 @@ static struct i2c_client *adv7604_dummy_client(struct v4l2_subdev *sd,
return i2c_new_dummy(client->adapter, io_read(sd, io_reg) >> 1);
}
+static const struct adv7604_reg_seq adv7604_recommended_settings_afe[] = {
+ /* reset ADI recommended settings for HDMI: */
+ /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x0d), 0x04 }, /* HDMI filter optimization */
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x0d), 0x04 }, /* HDMI filter optimization */
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x3d), 0x00 }, /* DDC bus active pull-up control */
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x3e), 0x74 }, /* TMDS PLL optimization */
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x4e), 0x3b }, /* TMDS PLL optimization */
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x57), 0x74 }, /* TMDS PLL optimization */
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x58), 0x63 }, /* TMDS PLL optimization */
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x8d), 0x18 }, /* equaliser */
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x8e), 0x34 }, /* equaliser */
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x93), 0x88 }, /* equaliser */
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x94), 0x2e }, /* equaliser */
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x96), 0x00 }, /* enable automatic EQ changing */
+
+ /* set ADI recommended settings for digitizer */
+ /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */
+ { ADV7604_REG(ADV7604_PAGE_AFE, 0x12), 0x7b }, /* ADC noise shaping filter controls */
+ { ADV7604_REG(ADV7604_PAGE_AFE, 0x0c), 0x1f }, /* CP core gain controls */
+ { ADV7604_REG(ADV7604_PAGE_CP, 0x3e), 0x04 }, /* CP core pre-gain control */
+ { ADV7604_REG(ADV7604_PAGE_CP, 0xc3), 0x39 }, /* CP coast control. Graphics mode */
+ { ADV7604_REG(ADV7604_PAGE_CP, 0x40), 0x5c }, /* CP core pre-gain control. Graphics mode */
+
+ { ADV7604_REG_SEQ_TERM, 0 },
+};
+
+static const struct adv7604_reg_seq adv7604_recommended_settings_hdmi[] = {
+ /* set ADI recommended settings for HDMI: */
+ /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x0d), 0x84 }, /* HDMI filter optimization */
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x3d), 0x10 }, /* DDC bus active pull-up control */
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x3e), 0x39 }, /* TMDS PLL optimization */
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x4e), 0x3b }, /* TMDS PLL optimization */
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x57), 0xb6 }, /* TMDS PLL optimization */
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x58), 0x03 }, /* TMDS PLL optimization */
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x8d), 0x18 }, /* equaliser */
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x8e), 0x34 }, /* equaliser */
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x93), 0x8b }, /* equaliser */
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x94), 0x2d }, /* equaliser */
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x96), 0x01 }, /* enable automatic EQ changing */
+
+ /* reset ADI recommended settings for digitizer */
+ /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */
+ { ADV7604_REG(ADV7604_PAGE_AFE, 0x12), 0xfb }, /* ADC noise shaping filter controls */
+ { ADV7604_REG(ADV7604_PAGE_AFE, 0x0c), 0x0d }, /* CP core gain controls */
+
+ { ADV7604_REG_SEQ_TERM, 0 },
+};
+
+static const struct adv7604_reg_seq adv7611_recommended_settings_hdmi[] = {
+ { ADV7604_REG(ADV7604_PAGE_CP, 0x6c), 0x00 },
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x6f), 0x0c },
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x87), 0x70 },
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x57), 0xda },
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x58), 0x01 },
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x03), 0x98 },
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x4c), 0x44 },
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x8d), 0x04 },
+ { ADV7604_REG(ADV7604_PAGE_HDMI, 0x8e), 0x1e },
+
+ { ADV7604_REG_SEQ_TERM, 0 },
+};
+
+static const struct adv7604_chip_info adv7604_chip_info[] = {
+ [ADV7604] = {
+ .type = ADV7604,
+ .has_afe = true,
+ .max_port = ADV7604_PAD_VGA_COMP,
+ .num_dv_ports = 4,
+ .edid_enable_reg = 0x77,
+ .edid_status_reg = 0x7d,
+ .lcf_reg = 0xb3,
+ .tdms_lock_mask = 0xe0,
+ .cable_det_mask = 0x1e,
+ .fmt_change_digital_mask = 0xc1,
+ .formats = adv7604_formats,
+ .nformats = ARRAY_SIZE(adv7604_formats),
+ .set_termination = adv7604_set_termination,
+ .setup_irqs = adv7604_setup_irqs,
+ .read_hdmi_pixelclock = adv7604_read_hdmi_pixelclock,
+ .read_cable_det = adv7604_read_cable_det,
+ .recommended_settings = {
+ [0] = adv7604_recommended_settings_afe,
+ [1] = adv7604_recommended_settings_hdmi,
+ },
+ .num_recommended_settings = {
+ [0] = ARRAY_SIZE(adv7604_recommended_settings_afe),
+ [1] = ARRAY_SIZE(adv7604_recommended_settings_hdmi),
+ },
+ .page_mask = BIT(ADV7604_PAGE_IO) | BIT(ADV7604_PAGE_AVLINK) |
+ BIT(ADV7604_PAGE_CEC) | BIT(ADV7604_PAGE_INFOFRAME) |
+ BIT(ADV7604_PAGE_ESDP) | BIT(ADV7604_PAGE_DPP) |
+ BIT(ADV7604_PAGE_AFE) | BIT(ADV7604_PAGE_REP) |
+ BIT(ADV7604_PAGE_EDID) | BIT(ADV7604_PAGE_HDMI) |
+ BIT(ADV7604_PAGE_TEST) | BIT(ADV7604_PAGE_CP) |
+ BIT(ADV7604_PAGE_VDP),
+ },
+ [ADV7611] = {
+ .type = ADV7611,
+ .has_afe = false,
+ .max_port = ADV7604_PAD_HDMI_PORT_A,
+ .num_dv_ports = 1,
+ .edid_enable_reg = 0x74,
+ .edid_status_reg = 0x76,
+ .lcf_reg = 0xa3,
+ .tdms_lock_mask = 0x43,
+ .cable_det_mask = 0x01,
+ .fmt_change_digital_mask = 0x03,
+ .formats = adv7611_formats,
+ .nformats = ARRAY_SIZE(adv7611_formats),
+ .set_termination = adv7611_set_termination,
+ .setup_irqs = adv7611_setup_irqs,
+ .read_hdmi_pixelclock = adv7611_read_hdmi_pixelclock,
+ .read_cable_det = adv7611_read_cable_det,
+ .recommended_settings = {
+ [1] = adv7611_recommended_settings_hdmi,
+ },
+ .num_recommended_settings = {
+ [1] = ARRAY_SIZE(adv7611_recommended_settings_hdmi),
+ },
+ .page_mask = BIT(ADV7604_PAGE_IO) | BIT(ADV7604_PAGE_CEC) |
+ BIT(ADV7604_PAGE_INFOFRAME) | BIT(ADV7604_PAGE_AFE) |
+ BIT(ADV7604_PAGE_REP) | BIT(ADV7604_PAGE_EDID) |
+ BIT(ADV7604_PAGE_HDMI) | BIT(ADV7604_PAGE_CP),
+ },
+};
+
+static struct i2c_device_id adv7604_i2c_id[] = {
+ { "adv7604", (kernel_ulong_t)&adv7604_chip_info[ADV7604] },
+ { "adv7611", (kernel_ulong_t)&adv7604_chip_info[ADV7611] },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adv7604_i2c_id);
+
+static struct of_device_id adv7604_of_id[] __maybe_unused = {
+ { .compatible = "adi,adv7611", .data = &adv7604_chip_info[ADV7611] },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adv7604_of_id);
+
+static int adv7604_parse_dt(struct adv7604_state *state)
+{
+ struct v4l2_of_endpoint bus_cfg;
+ struct device_node *endpoint;
+ struct device_node *np;
+ unsigned int flags;
+
+ np = state->i2c_clients[ADV7604_PAGE_IO]->dev.of_node;
+
+ /* Parse the endpoint. */
+ endpoint = of_graph_get_next_endpoint(np, NULL);
+ if (!endpoint)
+ return -EINVAL;
+
+ v4l2_of_parse_endpoint(endpoint, &bus_cfg);
+ of_node_put(endpoint);
+
+ flags = bus_cfg.bus.parallel.flags;
+
+ if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
+ state->pdata.inv_hs_pol = 1;
+
+ if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
+ state->pdata.inv_vs_pol = 1;
+
+ if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
+ state->pdata.inv_llc_pol = 1;
+
+ if (bus_cfg.bus_type == V4L2_MBUS_BT656) {
+ state->pdata.insert_av_codes = 1;
+ state->pdata.op_656_range = 1;
+ }
+
+ /* Disable the interrupt for now as no DT-based board uses it. */
+ state->pdata.int1_config = ADV7604_INT1_CONFIG_DISABLED;
+
+ /* Use the default I2C addresses. */
+ state->pdata.i2c_addresses[ADV7604_PAGE_AVLINK] = 0x42;
+ state->pdata.i2c_addresses[ADV7604_PAGE_CEC] = 0x40;
+ state->pdata.i2c_addresses[ADV7604_PAGE_INFOFRAME] = 0x3e;
+ state->pdata.i2c_addresses[ADV7604_PAGE_ESDP] = 0x38;
+ state->pdata.i2c_addresses[ADV7604_PAGE_DPP] = 0x3c;
+ state->pdata.i2c_addresses[ADV7604_PAGE_AFE] = 0x26;
+ state->pdata.i2c_addresses[ADV7604_PAGE_REP] = 0x32;
+ state->pdata.i2c_addresses[ADV7604_PAGE_EDID] = 0x36;
+ state->pdata.i2c_addresses[ADV7604_PAGE_HDMI] = 0x34;
+ state->pdata.i2c_addresses[ADV7604_PAGE_TEST] = 0x30;
+ state->pdata.i2c_addresses[ADV7604_PAGE_CP] = 0x22;
+ state->pdata.i2c_addresses[ADV7604_PAGE_VDP] = 0x24;
+
+ /* Hardcode the remaining platform data fields. */
+ state->pdata.disable_pwrdnb = 0;
+ state->pdata.disable_cable_det_rst = 0;
+ state->pdata.default_input = -1;
+ state->pdata.blank_data = 1;
+ state->pdata.alt_data_sat = 1;
+ state->pdata.op_format_mode_sel = ADV7604_OP_FORMAT_MODE0;
+ state->pdata.bus_order = ADV7604_BUS_ORDER_RGB;
+
+ return 0;
+}
+
static int adv7604_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
static const struct v4l2_dv_timings cea640x480 =
V4L2_DV_BT_CEA_640X480P59_94;
struct adv7604_state *state;
- struct adv7604_platform_data *pdata = client->dev.platform_data;
struct v4l2_ctrl_handler *hdl;
struct v4l2_subdev *sd;
+ unsigned int i;
+ u16 val;
int err;
/* Check if the adapter supports the needed features */
@@ -2227,32 +2764,80 @@ static int adv7604_probe(struct i2c_client *client,
return -ENOMEM;
}
+ state->i2c_clients[ADV7604_PAGE_IO] = client;
+
/* initialize variables */
state->restart_stdi_once = true;
state->selected_input = ~0;
- /* platform data */
- if (!pdata) {
+ if (IS_ENABLED(CONFIG_OF) && client->dev.of_node) {
+ const struct of_device_id *oid;
+
+ oid = of_match_node(adv7604_of_id, client->dev.of_node);
+ state->info = oid->data;
+
+ err = adv7604_parse_dt(state);
+ if (err < 0) {
+ v4l_err(client, "DT parsing error\n");
+ return err;
+ }
+ } else if (client->dev.platform_data) {
+ struct adv7604_platform_data *pdata = client->dev.platform_data;
+
+ state->info = (const struct adv7604_chip_info *)id->driver_data;
+ state->pdata = *pdata;
+ } else {
v4l_err(client, "No platform data!\n");
return -ENODEV;
}
- state->pdata = *pdata;
+
+ /* Request GPIOs. */
+ for (i = 0; i < state->info->num_dv_ports; ++i) {
+ state->hpd_gpio[i] =
+ devm_gpiod_get_index(&client->dev, "hpd", i);
+ if (IS_ERR(state->hpd_gpio[i]))
+ continue;
+
+ gpiod_direction_output(state->hpd_gpio[i], 0);
+
+ v4l_info(client, "Handling HPD %u GPIO\n", i);
+ }
+
state->timings = cea640x480;
+ state->format = adv7604_format_info(state, V4L2_MBUS_FMT_YUYV8_2X8);
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &adv7604_ops);
+ snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
+ id->name, i2c_adapter_id(client->adapter),
+ client->addr);
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- /* i2c access to adv7604? */
- if (adv_smbus_read_byte_data_check(client, 0xfb, false) != 0x68) {
- v4l2_info(sd, "not an adv7604 on address 0x%x\n",
- client->addr << 1);
- return -ENODEV;
+ /*
+ * Verify that the chip is present. On ADV7604 the RD_INFO register only
+ * identifies the revision, while on ADV7611 it identifies the model as
+ * well. Use the HDMI slave address on ADV7604 and RD_INFO on ADV7611.
+ */
+ if (state->info->type == ADV7604) {
+ val = adv_smbus_read_byte_data_check(client, 0xfb, false);
+ if (val != 0x68) {
+ v4l2_info(sd, "not an adv7604 on address 0x%x\n",
+ client->addr << 1);
+ return -ENODEV;
+ }
+ } else {
+ val = (adv_smbus_read_byte_data_check(client, 0xea, false) << 8)
+ | (adv_smbus_read_byte_data_check(client, 0xeb, false) << 0);
+ if (val != 0x2051) {
+ v4l2_info(sd, "not an adv7611 on address 0x%x\n",
+ client->addr << 1);
+ return -ENODEV;
+ }
}
/* control handlers */
hdl = &state->hdl;
- v4l2_ctrl_handler_init(hdl, 9);
+ v4l2_ctrl_handler_init(hdl, adv7604_has_afe(state) ? 9 : 8);
v4l2_ctrl_new_std(hdl, &adv7604_ctrl_ops,
V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
@@ -2265,15 +2850,17 @@ static int adv7604_probe(struct i2c_client *client,
/* private controls */
state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL,
- V4L2_CID_DV_RX_POWER_PRESENT, 0, 0x0f, 0, 0);
+ V4L2_CID_DV_RX_POWER_PRESENT, 0,
+ (1 << state->info->num_dv_ports) - 1, 0, 0);
state->rgb_quantization_range_ctrl =
v4l2_ctrl_new_std_menu(hdl, &adv7604_ctrl_ops,
V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL,
0, V4L2_DV_RGB_RANGE_AUTO);
/* custom controls */
- state->analog_sampling_phase_ctrl =
- v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_analog_sampling_phase, NULL);
+ if (adv7604_has_afe(state))
+ state->analog_sampling_phase_ctrl =
+ v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_analog_sampling_phase, NULL);
state->free_run_color_manual_ctrl =
v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_free_run_color_manual, NULL);
state->free_run_color_ctrl =
@@ -2286,7 +2873,8 @@ static int adv7604_probe(struct i2c_client *client,
}
state->detect_tx_5v_ctrl->is_private = true;
state->rgb_quantization_range_ctrl->is_private = true;
- state->analog_sampling_phase_ctrl->is_private = true;
+ if (adv7604_has_afe(state))
+ state->analog_sampling_phase_ctrl->is_private = true;
state->free_run_color_manual_ctrl->is_private = true;
state->free_run_color_ctrl->is_private = true;
@@ -2295,25 +2883,18 @@ static int adv7604_probe(struct i2c_client *client,
goto err_hdl;
}
- state->i2c_avlink = adv7604_dummy_client(sd, pdata->i2c_avlink, 0xf3);
- state->i2c_cec = adv7604_dummy_client(sd, pdata->i2c_cec, 0xf4);
- state->i2c_infoframe = adv7604_dummy_client(sd, pdata->i2c_infoframe, 0xf5);
- state->i2c_esdp = adv7604_dummy_client(sd, pdata->i2c_esdp, 0xf6);
- state->i2c_dpp = adv7604_dummy_client(sd, pdata->i2c_dpp, 0xf7);
- state->i2c_afe = adv7604_dummy_client(sd, pdata->i2c_afe, 0xf8);
- state->i2c_repeater = adv7604_dummy_client(sd, pdata->i2c_repeater, 0xf9);
- state->i2c_edid = adv7604_dummy_client(sd, pdata->i2c_edid, 0xfa);
- state->i2c_hdmi = adv7604_dummy_client(sd, pdata->i2c_hdmi, 0xfb);
- state->i2c_test = adv7604_dummy_client(sd, pdata->i2c_test, 0xfc);
- state->i2c_cp = adv7604_dummy_client(sd, pdata->i2c_cp, 0xfd);
- state->i2c_vdp = adv7604_dummy_client(sd, pdata->i2c_vdp, 0xfe);
- if (!state->i2c_avlink || !state->i2c_cec || !state->i2c_infoframe ||
- !state->i2c_esdp || !state->i2c_dpp || !state->i2c_afe ||
- !state->i2c_repeater || !state->i2c_edid || !state->i2c_hdmi ||
- !state->i2c_test || !state->i2c_cp || !state->i2c_vdp) {
- err = -ENOMEM;
- v4l2_err(sd, "failed to create all i2c clients\n");
- goto err_i2c;
+ for (i = 1; i < ADV7604_PAGE_MAX; ++i) {
+ if (!(BIT(i) & state->info->page_mask))
+ continue;
+
+ state->i2c_clients[i] =
+ adv7604_dummy_client(sd, state->pdata.i2c_addresses[i],
+ 0xf2 + i);
+ if (state->i2c_clients[i] == NULL) {
+ err = -ENOMEM;
+ v4l2_err(sd, "failed to create i2c client %u\n", i);
+ goto err_i2c;
+ }
}
/* work queues */
@@ -2327,8 +2908,14 @@ static int adv7604_probe(struct i2c_client *client,
INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug,
adv7604_delayed_work_enable_hotplug);
- state->pad.flags = MEDIA_PAD_FL_SOURCE;
- err = media_entity_init(&sd->entity, 1, &state->pad, 0);
+ state->source_pad = state->info->num_dv_ports
+ + (state->info->has_afe ? 2 : 0);
+ for (i = 0; i < state->source_pad; ++i)
+ state->pads[i].flags = MEDIA_PAD_FL_SINK;
+ state->pads[state->source_pad].flags = MEDIA_PAD_FL_SOURCE;
+
+ err = media_entity_init(&sd->entity, state->source_pad + 1,
+ state->pads, 0);
if (err)
goto err_work_queues;
@@ -2337,6 +2924,11 @@ static int adv7604_probe(struct i2c_client *client,
goto err_entity;
v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
client->addr << 1, client->adapter->name);
+
+ err = v4l2_async_register_subdev(sd);
+ if (err)
+ goto err_entity;
+
return 0;
err_entity:
@@ -2360,6 +2952,7 @@ static int adv7604_remove(struct i2c_client *client)
cancel_delayed_work(&state->delayed_work_enable_hotplug);
destroy_workqueue(state->work_queues);
+ v4l2_async_unregister_subdev(sd);
v4l2_device_unregister_subdev(sd);
media_entity_cleanup(&sd->entity);
adv7604_unregister_clients(to_state(sd));
@@ -2369,20 +2962,15 @@ static int adv7604_remove(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
-static struct i2c_device_id adv7604_id[] = {
- { "adv7604", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, adv7604_id);
-
static struct i2c_driver adv7604_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "adv7604",
+ .of_match_table = of_match_ptr(adv7604_of_id),
},
.probe = adv7604_probe,
.remove = adv7604_remove,
- .id_table = adv7604_id,
+ .id_table = adv7604_i2c_id,
};
module_i2c_driver(adv7604_driver);
diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c
index 1effc21e1cd..0d554919cdd 100644
--- a/drivers/media/i2c/adv7842.c
+++ b/drivers/media/i2c/adv7842.c
@@ -546,6 +546,14 @@ static void main_reset(struct v4l2_subdev *sd)
/* ----------------------------------------------------------------------- */
+static inline bool is_analog_input(struct v4l2_subdev *sd)
+{
+ struct adv7842_state *state = to_state(sd);
+
+ return ((state->mode == ADV7842_MODE_RGB) ||
+ (state->mode == ADV7842_MODE_COMP));
+}
+
static inline bool is_digital_input(struct v4l2_subdev *sd)
{
struct adv7842_state *state = to_state(sd);
@@ -1027,12 +1035,72 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd,
cp_write(sd, 0xac, (height & 0x0f) << 4);
}
+static void adv7842_set_offset(struct v4l2_subdev *sd, bool auto_offset, u16 offset_a, u16 offset_b, u16 offset_c)
+{
+ struct adv7842_state *state = to_state(sd);
+ u8 offset_buf[4];
+
+ if (auto_offset) {
+ offset_a = 0x3ff;
+ offset_b = 0x3ff;
+ offset_c = 0x3ff;
+ }
+
+ v4l2_dbg(2, debug, sd, "%s: %s offset: a = 0x%x, b = 0x%x, c = 0x%x\n",
+ __func__, auto_offset ? "Auto" : "Manual",
+ offset_a, offset_b, offset_c);
+
+ offset_buf[0]= (cp_read(sd, 0x77) & 0xc0) | ((offset_a & 0x3f0) >> 4);
+ offset_buf[1] = ((offset_a & 0x00f) << 4) | ((offset_b & 0x3c0) >> 6);
+ offset_buf[2] = ((offset_b & 0x03f) << 2) | ((offset_c & 0x300) >> 8);
+ offset_buf[3] = offset_c & 0x0ff;
+
+ /* Registers must be written in this order with no i2c access in between */
+ if (adv_smbus_write_i2c_block_data(state->i2c_cp, 0x77, 4, offset_buf))
+ v4l2_err(sd, "%s: i2c error writing to CP reg 0x77, 0x78, 0x79, 0x7a\n", __func__);
+}
+
+static void adv7842_set_gain(struct v4l2_subdev *sd, bool auto_gain, u16 gain_a, u16 gain_b, u16 gain_c)
+{
+ struct adv7842_state *state = to_state(sd);
+ u8 gain_buf[4];
+ u8 gain_man = 1;
+ u8 agc_mode_man = 1;
+
+ if (auto_gain) {
+ gain_man = 0;
+ agc_mode_man = 0;
+ gain_a = 0x100;
+ gain_b = 0x100;
+ gain_c = 0x100;
+ }
+
+ v4l2_dbg(2, debug, sd, "%s: %s gain: a = 0x%x, b = 0x%x, c = 0x%x\n",
+ __func__, auto_gain ? "Auto" : "Manual",
+ gain_a, gain_b, gain_c);
+
+ gain_buf[0] = ((gain_man << 7) | (agc_mode_man << 6) | ((gain_a & 0x3f0) >> 4));
+ gain_buf[1] = (((gain_a & 0x00f) << 4) | ((gain_b & 0x3c0) >> 6));
+ gain_buf[2] = (((gain_b & 0x03f) << 2) | ((gain_c & 0x300) >> 8));
+ gain_buf[3] = ((gain_c & 0x0ff));
+
+ /* Registers must be written in this order with no i2c access in between */
+ if (adv_smbus_write_i2c_block_data(state->i2c_cp, 0x73, 4, gain_buf))
+ v4l2_err(sd, "%s: i2c error writing to CP reg 0x73, 0x74, 0x75, 0x76\n", __func__);
+}
+
static void set_rgb_quantization_range(struct v4l2_subdev *sd)
{
struct adv7842_state *state = to_state(sd);
+ bool rgb_output = io_read(sd, 0x02) & 0x02;
+ bool hdmi_signal = hdmi_read(sd, 0x05) & 0x80;
- v4l2_dbg(2, debug, sd, "%s: rgb_quantization_range = %d\n",
- __func__, state->rgb_quantization_range);
+ v4l2_dbg(2, debug, sd, "%s: RGB quantization range: %d, RGB out: %d, HDMI: %d\n",
+ __func__, state->rgb_quantization_range,
+ rgb_output, hdmi_signal);
+
+ adv7842_set_gain(sd, true, 0x0, 0x0, 0x0);
+ adv7842_set_offset(sd, true, 0x0, 0x0, 0x0);
switch (state->rgb_quantization_range) {
case V4L2_DV_RGB_RANGE_AUTO:
@@ -1050,7 +1118,7 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd)
break;
}
- if (hdmi_read(sd, 0x05) & 0x80) {
+ if (hdmi_signal) {
/* Receiving HDMI signal
* Set automode */
io_write_and_or(sd, 0x02, 0x0f, 0xf0);
@@ -1066,24 +1134,45 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd)
} else {
/* RGB full range (0-255) */
io_write_and_or(sd, 0x02, 0x0f, 0x10);
+
+ if (is_digital_input(sd) && rgb_output) {
+ adv7842_set_offset(sd, false, 0x40, 0x40, 0x40);
+ } else {
+ adv7842_set_gain(sd, false, 0xe0, 0xe0, 0xe0);
+ adv7842_set_offset(sd, false, 0x70, 0x70, 0x70);
+ }
}
break;
case V4L2_DV_RGB_RANGE_LIMITED:
if (state->mode == ADV7842_MODE_COMP) {
/* YCrCb limited range (16-235) */
io_write_and_or(sd, 0x02, 0x0f, 0x20);
- } else {
- /* RGB limited range (16-235) */
- io_write_and_or(sd, 0x02, 0x0f, 0x00);
+ break;
}
+
+ /* RGB limited range (16-235) */
+ io_write_and_or(sd, 0x02, 0x0f, 0x00);
+
break;
case V4L2_DV_RGB_RANGE_FULL:
if (state->mode == ADV7842_MODE_COMP) {
/* YCrCb full range (0-255) */
io_write_and_or(sd, 0x02, 0x0f, 0x60);
+ break;
+ }
+
+ /* RGB full range (0-255) */
+ io_write_and_or(sd, 0x02, 0x0f, 0x10);
+
+ if (is_analog_input(sd) || hdmi_signal)
+ break;
+
+ /* Adjust gain/offset for DVI-D signals only */
+ if (rgb_output) {
+ adv7842_set_offset(sd, false, 0x40, 0x40, 0x40);
} else {
- /* RGB full range (0-255) */
- io_write_and_or(sd, 0x02, 0x0f, 0x10);
+ adv7842_set_gain(sd, false, 0xe0, 0xe0, 0xe0);
+ adv7842_set_offset(sd, false, 0x70, 0x70, 0x70);
}
break;
}
@@ -1310,6 +1399,9 @@ static int read_stdi(struct v4l2_subdev *sd, struct stdi_readback *stdi)
static int adv7842_enum_dv_timings(struct v4l2_subdev *sd,
struct v4l2_enum_dv_timings *timings)
{
+ if (timings->pad != 0)
+ return -EINVAL;
+
return v4l2_enum_dv_timings_cap(timings,
adv7842_get_dv_timings_cap(sd), adv7842_check_dv_timings, NULL);
}
@@ -1317,6 +1409,9 @@ static int adv7842_enum_dv_timings(struct v4l2_subdev *sd,
static int adv7842_dv_timings_cap(struct v4l2_subdev *sd,
struct v4l2_dv_timings_cap *cap)
{
+ if (cap->pad != 0)
+ return -EINVAL;
+
*cap = *adv7842_get_dv_timings_cap(sd);
return 0;
}
@@ -1360,12 +1455,11 @@ static int adv7842_query_dv_timings(struct v4l2_subdev *sd,
bt->width = (hdmi_read(sd, 0x07) & 0x0f) * 256 + hdmi_read(sd, 0x08);
bt->height = (hdmi_read(sd, 0x09) & 0x0f) * 256 + hdmi_read(sd, 0x0a);
- freq = (hdmi_read(sd, 0x06) * 1000000) +
- ((hdmi_read(sd, 0x3b) & 0x30) >> 4) * 250000;
-
+ freq = ((hdmi_read(sd, 0x51) << 1) + (hdmi_read(sd, 0x52) >> 7)) * 1000000;
+ freq += ((hdmi_read(sd, 0x52) & 0x7f) * 7813);
if (is_hdmi(sd)) {
/* adjust for deep color mode */
- freq = freq * 8 / (((hdmi_read(sd, 0x0b) & 0xc0) >> 5) + 8);
+ freq = freq * 8 / (((hdmi_read(sd, 0x0b) & 0xc0) >> 6) * 2 + 8);
}
bt->pixelclock = freq;
bt->hfrontporch = (hdmi_read(sd, 0x20) & 0x03) * 256 +
@@ -1717,8 +1811,8 @@ static void select_input(struct v4l2_subdev *sd,
* (rev. 2.5, June 2010)" p. 17. */
afe_write(sd, 0x12, 0xfb); /* ADC noise shaping filter controls */
afe_write(sd, 0x0c, 0x0d); /* CP core gain controls */
- cp_write(sd, 0x3e, 0x80); /* CP core pre-gain control,
- enable color control */
+ cp_write(sd, 0x3e, 0x00); /* CP core pre-gain control */
+
/* CP coast control */
cp_write(sd, 0xc3, 0x33); /* Component mode */
@@ -1912,6 +2006,7 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
if (irq_status[5] & 0x08) {
v4l2_dbg(1, debug, sd, "%s: irq %s mode\n", __func__,
(io_read(sd, 0x65) & 0x08) ? "HDMI" : "DVI");
+ set_rgb_quantization_range(sd);
if (handled)
*handled = true;
}
@@ -1926,7 +2021,7 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
return 0;
}
-static int adv7842_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid)
+static int adv7842_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
{
struct adv7842_state *state = to_state(sd);
u8 *data = NULL;
@@ -1941,8 +2036,6 @@ static int adv7842_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edi
return -EINVAL;
if (edid->start_block == 1)
edid->blocks = 1;
- if (!edid->edid)
- return -EINVAL;
switch (edid->pad) {
case ADV7842_EDID_PORT_A:
@@ -1966,7 +2059,7 @@ static int adv7842_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edi
return 0;
}
-static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *e)
+static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *e)
{
struct adv7842_state *state = to_state(sd);
int err = 0;
@@ -1977,8 +2070,6 @@ static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *e)
return -EINVAL;
if (e->blocks > 2)
return -E2BIG;
- if (!e->edid)
- return -EINVAL;
/* todo, per edid */
state->aspect_ratio = v4l2_calc_aspect_ratio(e->edid[0x15],
@@ -2103,7 +2194,8 @@ static void print_avi_infoframe(struct v4l2_subdev *sd)
{
int i;
uint8_t buf[14];
- uint8_t avi_inf_len;
+ u8 avi_len;
+ u8 avi_ver;
struct avi_info_frame avi;
if (!(hdmi_read(sd, 0x05) & 0x80)) {
@@ -2116,18 +2208,20 @@ static void print_avi_infoframe(struct v4l2_subdev *sd)
}
if (io_read(sd, 0x88) & 0x10) {
- /* Note: the ADV7842 calculated incorrect checksums for InfoFrames
- with a length of 14 or 15. See the ADV7842 Register Settings
- Recommendations document for more details. */
- v4l2_info(sd, "AVI infoframe checksum error\n");
- return;
+ v4l2_info(sd, "AVI infoframe checksum error has occurred earlier\n");
+ io_write(sd, 0x8a, 0x10); /* clear AVI_INF_CKS_ERR_RAW */
+ if (io_read(sd, 0x88) & 0x10) {
+ v4l2_info(sd, "AVI infoframe checksum error still present\n");
+ io_write(sd, 0x8a, 0x10); /* clear AVI_INF_CKS_ERR_RAW */
+ }
}
- avi_inf_len = infoframe_read(sd, 0xe2);
+ avi_len = infoframe_read(sd, 0xe2);
+ avi_ver = infoframe_read(sd, 0xe1);
v4l2_info(sd, "AVI infoframe version %d (%d byte)\n",
- infoframe_read(sd, 0xe1), avi_inf_len);
+ avi_ver, avi_len);
- if (infoframe_read(sd, 0xe1) != 0x02)
+ if (avi_ver != 0x02)
return;
for (i = 0; i < 14; i++)
@@ -2519,6 +2613,12 @@ static int adv7842_core_init(struct v4l2_subdev *sd)
disable_input(sd);
+ /*
+ * Disable I2C access to internal EDID ram from HDMI DDC ports
+ * Disable auto edid enable when leaving powerdown mode
+ */
+ rep_write_and_or(sd, 0x77, 0xd3, 0x20);
+
/* power */
io_write(sd, 0x0c, 0x42); /* Power up part and power down VDP */
io_write(sd, 0x15, 0x80); /* Power up pads */
@@ -2554,7 +2654,7 @@ static int adv7842_core_init(struct v4l2_subdev *sd)
sdp_write_and_or(sd, 0xdd, 0xf0, pdata->sdp_free_run_force |
(pdata->sdp_free_run_cbar_en << 1) |
(pdata->sdp_free_run_man_col_en << 2) |
- (pdata->sdp_free_run_force << 3));
+ (pdata->sdp_free_run_auto << 3));
/* TODO from platform data */
cp_write(sd, 0x69, 0x14); /* Enable CP CSC */
@@ -2599,12 +2699,15 @@ static int adv7842_core_init(struct v4l2_subdev *sd)
enable_input(sd);
- /* disable I2C access to internal EDID ram from HDMI DDC ports */
- rep_write_and_or(sd, 0x77, 0xf3, 0x00);
-
- hdmi_write(sd, 0x69, 0xa3); /* HPA manual */
- /* HPA disable on port A and B */
- io_write_and_or(sd, 0x20, 0xcf, 0x00);
+ if (pdata->hpa_auto) {
+ /* HPA auto, HPA 0.5s after Edid set and Cable detect */
+ hdmi_write(sd, 0x69, 0x5c);
+ } else {
+ /* HPA manual */
+ hdmi_write(sd, 0x69, 0xa3);
+ /* HPA disable on port A and B */
+ io_write_and_or(sd, 0x20, 0xcf, 0x00);
+ }
/* LLC */
io_write(sd, 0x19, 0x80 | pdata->llc_dll_phase);
@@ -2772,8 +2875,6 @@ static const struct v4l2_ctrl_ops adv7842_ctrl_ops = {
static const struct v4l2_subdev_core_ops adv7842_core_ops = {
.log_status = adv7842_log_status,
- .g_std = adv7842_g_std,
- .s_std = adv7842_s_std,
.ioctl = adv7842_ioctl,
.interrupt_service_routine = adv7842_isr,
#ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -2783,14 +2884,14 @@ static const struct v4l2_subdev_core_ops adv7842_core_ops = {
};
static const struct v4l2_subdev_video_ops adv7842_video_ops = {
+ .g_std = adv7842_g_std,
+ .s_std = adv7842_s_std,
.s_routing = adv7842_s_routing,
.querystd = adv7842_querystd,
.g_input_status = adv7842_g_input_status,
.s_dv_timings = adv7842_s_dv_timings,
.g_dv_timings = adv7842_g_dv_timings,
.query_dv_timings = adv7842_query_dv_timings,
- .enum_dv_timings = adv7842_enum_dv_timings,
- .dv_timings_cap = adv7842_dv_timings_cap,
.enum_mbus_fmt = adv7842_enum_mbus_fmt,
.g_mbus_fmt = adv7842_g_mbus_fmt,
.try_mbus_fmt = adv7842_g_mbus_fmt,
@@ -2800,6 +2901,8 @@ static const struct v4l2_subdev_video_ops adv7842_video_ops = {
static const struct v4l2_subdev_pad_ops adv7842_pad_ops = {
.get_edid = adv7842_get_edid,
.set_edid = adv7842_set_edid,
+ .enum_dv_timings = adv7842_enum_dv_timings,
+ .dv_timings_cap = adv7842_dv_timings_cap,
};
static const struct v4l2_subdev_ops adv7842_ops = {
diff --git a/drivers/media/i2c/bt819.c b/drivers/media/i2c/bt819.c
index 369cf6ff88f..76b334a6a56 100644
--- a/drivers/media/i2c/bt819.c
+++ b/drivers/media/i2c/bt819.c
@@ -387,10 +387,10 @@ static const struct v4l2_subdev_core_ops bt819_core_ops = {
.s_ctrl = v4l2_subdev_s_ctrl,
.queryctrl = v4l2_subdev_queryctrl,
.querymenu = v4l2_subdev_querymenu,
- .s_std = bt819_s_std,
};
static const struct v4l2_subdev_video_ops bt819_video_ops = {
+ .s_std = bt819_s_std,
.s_routing = bt819_s_routing,
.s_stream = bt819_s_stream,
.querystd = bt819_querystd,
diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c
index 2e3771d5735..e453a3ffe7d 100644
--- a/drivers/media/i2c/cx25840/cx25840-core.c
+++ b/drivers/media/i2c/cx25840/cx25840-core.c
@@ -5041,8 +5041,6 @@ static const struct v4l2_subdev_core_ops cx25840_core_ops = {
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
.queryctrl = v4l2_subdev_queryctrl,
.querymenu = v4l2_subdev_querymenu,
- .s_std = cx25840_s_std,
- .g_std = cx25840_g_std,
.reset = cx25840_reset,
.load_fw = cx25840_load_fw,
.s_io_pin_config = common_s_io_pin_config,
@@ -5067,6 +5065,8 @@ static const struct v4l2_subdev_audio_ops cx25840_audio_ops = {
};
static const struct v4l2_subdev_video_ops cx25840_video_ops = {
+ .s_std = cx25840_s_std,
+ .g_std = cx25840_g_std,
.s_routing = cx25840_s_video_routing,
.s_mbus_fmt = cx25840_s_mbus_fmt,
.s_stream = cx25840_s_stream,
diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c
index 99ee456700f..c8fe1358ec9 100644
--- a/drivers/media/i2c/ir-kbd-i2c.c
+++ b/drivers/media/i2c/ir-kbd-i2c.c
@@ -431,8 +431,8 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
* Initialize the other fields of rc_dev
*/
rc->map_name = ir->ir_codes;
- rc->allowed_protos = rc_type;
- rc->enabled_protocols = rc_type;
+ rc_set_allowed_protocols(rc, rc_type);
+ rc_set_enabled_protocols(rc, rc_type);
if (!rc->driver_name)
rc->driver_name = MODULE_NAME;
diff --git a/drivers/media/i2c/ks0127.c b/drivers/media/i2c/ks0127.c
index c3e94ae82c0..25b81bc58c8 100644
--- a/drivers/media/i2c/ks0127.c
+++ b/drivers/media/i2c/ks0127.c
@@ -648,11 +648,8 @@ static int ks0127_g_input_status(struct v4l2_subdev *sd, u32 *status)
/* ----------------------------------------------------------------------- */
-static const struct v4l2_subdev_core_ops ks0127_core_ops = {
- .s_std = ks0127_s_std,
-};
-
static const struct v4l2_subdev_video_ops ks0127_video_ops = {
+ .s_std = ks0127_s_std,
.s_routing = ks0127_s_routing,
.s_stream = ks0127_s_stream,
.querystd = ks0127_querystd,
@@ -660,7 +657,6 @@ static const struct v4l2_subdev_video_ops ks0127_video_ops = {
};
static const struct v4l2_subdev_ops ks0127_ops = {
- .core = &ks0127_core_ops,
.video = &ks0127_video_ops,
};
diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c
index d98ca3aebe2..c23de593c17 100644
--- a/drivers/media/i2c/lm3560.c
+++ b/drivers/media/i2c/lm3560.c
@@ -15,12 +15,6 @@
* 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/delay.h>
@@ -42,7 +36,7 @@
#define REG_FLAG 0xd0
#define REG_CONFIG1 0xe0
-/* Fault Mask */
+/* fault mask */
#define FAULT_TIMEOUT (1<<0)
#define FAULT_OVERTEMP (1<<1)
#define FAULT_SHORT_CIRCUIT (1<<2)
@@ -53,7 +47,8 @@ enum led_enable {
MODE_FLASH = 0x3,
};
-/* struct lm3560_flash
+/**
+ * struct lm3560_flash
*
* @pdata: platform data
* @regmap: reg. map for i2c
@@ -98,7 +93,7 @@ static int lm3560_mode_ctrl(struct lm3560_flash *flash)
return rval;
}
-/* led1/2 enable/disable */
+/* led1/2 enable/disable */
static int lm3560_enable_ctrl(struct lm3560_flash *flash,
enum lm3560_led_id led_no, bool on)
{
@@ -168,7 +163,7 @@ static int lm3560_flash_brt_ctrl(struct lm3560_flash *flash,
return rval;
}
-/* V4L2 controls */
+/* v4l2 controls */
static int lm3560_get_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no)
{
struct lm3560_flash *flash = to_lm3560_flash(ctrl, led_no);
@@ -297,6 +292,7 @@ static int lm3560_init_controls(struct lm3560_flash *flash,
const struct v4l2_ctrl_ops *ops = &lm3560_led_ctrl_ops[led_no];
v4l2_ctrl_handler_init(hdl, 8);
+
/* flash mode */
v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_LED_MODE,
V4L2_FLASH_LED_MODE_TORCH, ~0x7,
@@ -309,6 +305,7 @@ static int lm3560_init_controls(struct lm3560_flash *flash,
/* flash strobe */
v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_STROBE, 0, 0, 0, 0);
+
/* flash strobe stop */
v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0);
@@ -395,7 +392,7 @@ static int lm3560_init_device(struct lm3560_flash *flash)
rval = lm3560_mode_ctrl(flash);
if (rval < 0)
return rval;
- /* Reset faults */
+ /* reset faults */
rval = regmap_read(flash->regmap, REG_FLAG, &reg_val);
return rval;
}
@@ -419,8 +416,7 @@ static int lm3560_probe(struct i2c_client *client,
/* if there is no platform data, use chip default value */
if (pdata == NULL) {
- pdata =
- kzalloc(sizeof(struct lm3560_platform_data), GFP_KERNEL);
+ pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
if (pdata == NULL)
return -ENODEV;
pdata->peak = LM3560_PEAK_3600mA;
diff --git a/drivers/media/i2c/lm3646.c b/drivers/media/i2c/lm3646.c
new file mode 100644
index 00000000000..626fb4679c0
--- /dev/null
+++ b/drivers/media/i2c/lm3646.c
@@ -0,0 +1,414 @@
+/*
+ * drivers/media/i2c/lm3646.c
+ * General device driver for TI lm3646, Dual FLASH LED Driver
+ *
+ * Copyright (C) 2014 Texas Instruments
+ *
+ * Contact: Daniel Jeong <gshark.jeong@gmail.com>
+ * Ldd-Mlp <ldd-mlp@list.ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/videodev2.h>
+#include <media/lm3646.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+
+/* registers definitions */
+#define REG_ENABLE 0x01
+#define REG_TORCH_BR 0x05
+#define REG_FLASH_BR 0x05
+#define REG_FLASH_TOUT 0x04
+#define REG_FLAG 0x08
+#define REG_STROBE_SRC 0x06
+#define REG_LED1_FLASH_BR 0x06
+#define REG_LED1_TORCH_BR 0x07
+
+#define MASK_ENABLE 0x03
+#define MASK_TORCH_BR 0x70
+#define MASK_FLASH_BR 0x0F
+#define MASK_FLASH_TOUT 0x07
+#define MASK_FLAG 0xFF
+#define MASK_STROBE_SRC 0x80
+
+/* Fault Mask */
+#define FAULT_TIMEOUT (1<<0)
+#define FAULT_SHORT_CIRCUIT (1<<1)
+#define FAULT_UVLO (1<<2)
+#define FAULT_IVFM (1<<3)
+#define FAULT_OCP (1<<4)
+#define FAULT_OVERTEMP (1<<5)
+#define FAULT_NTC_TRIP (1<<6)
+#define FAULT_OVP (1<<7)
+
+enum led_mode {
+ MODE_SHDN = 0x0,
+ MODE_TORCH = 0x2,
+ MODE_FLASH = 0x3,
+};
+
+/*
+ * struct lm3646_flash
+ *
+ * @pdata: platform data
+ * @regmap: reg. map for i2c
+ * @lock: muxtex for serial access.
+ * @led_mode: V4L2 LED mode
+ * @ctrls_led: V4L2 contols
+ * @subdev_led: V4L2 subdev
+ * @mode_reg : mode register value
+ */
+struct lm3646_flash {
+ struct device *dev;
+ struct lm3646_platform_data *pdata;
+ struct regmap *regmap;
+
+ struct v4l2_ctrl_handler ctrls_led;
+ struct v4l2_subdev subdev_led;
+
+ u8 mode_reg;
+};
+
+#define to_lm3646_flash(_ctrl) \
+ container_of(_ctrl->handler, struct lm3646_flash, ctrls_led)
+
+/* enable mode control */
+static int lm3646_mode_ctrl(struct lm3646_flash *flash,
+ enum v4l2_flash_led_mode led_mode)
+{
+ switch (led_mode) {
+ case V4L2_FLASH_LED_MODE_NONE:
+ return regmap_write(flash->regmap,
+ REG_ENABLE, flash->mode_reg | MODE_SHDN);
+ case V4L2_FLASH_LED_MODE_TORCH:
+ return regmap_write(flash->regmap,
+ REG_ENABLE, flash->mode_reg | MODE_TORCH);
+ case V4L2_FLASH_LED_MODE_FLASH:
+ return regmap_write(flash->regmap,
+ REG_ENABLE, flash->mode_reg | MODE_FLASH);
+ }
+ return -EINVAL;
+}
+
+/* V4L2 controls */
+static int lm3646_get_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct lm3646_flash *flash = to_lm3646_flash(ctrl);
+ unsigned int reg_val;
+ int rval;
+
+ if (ctrl->id != V4L2_CID_FLASH_FAULT)
+ return -EINVAL;
+
+ rval = regmap_read(flash->regmap, REG_FLAG, &reg_val);
+ if (rval < 0)
+ return rval;
+
+ ctrl->val = 0;
+ if (reg_val & FAULT_TIMEOUT)
+ ctrl->val |= V4L2_FLASH_FAULT_TIMEOUT;
+ if (reg_val & FAULT_SHORT_CIRCUIT)
+ ctrl->val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
+ if (reg_val & FAULT_UVLO)
+ ctrl->val |= V4L2_FLASH_FAULT_UNDER_VOLTAGE;
+ if (reg_val & FAULT_IVFM)
+ ctrl->val |= V4L2_FLASH_FAULT_INPUT_VOLTAGE;
+ if (reg_val & FAULT_OCP)
+ ctrl->val |= V4L2_FLASH_FAULT_OVER_CURRENT;
+ if (reg_val & FAULT_OVERTEMP)
+ ctrl->val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE;
+ if (reg_val & FAULT_NTC_TRIP)
+ ctrl->val |= V4L2_FLASH_FAULT_LED_OVER_TEMPERATURE;
+ if (reg_val & FAULT_OVP)
+ ctrl->val |= V4L2_FLASH_FAULT_OVER_VOLTAGE;
+
+ return 0;
+}
+
+static int lm3646_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct lm3646_flash *flash = to_lm3646_flash(ctrl);
+ unsigned int reg_val;
+ int rval = -EINVAL;
+
+ switch (ctrl->id) {
+ case V4L2_CID_FLASH_LED_MODE:
+
+ if (ctrl->val != V4L2_FLASH_LED_MODE_FLASH)
+ return lm3646_mode_ctrl(flash, ctrl->val);
+ /* switch to SHDN mode before flash strobe on */
+ return lm3646_mode_ctrl(flash, V4L2_FLASH_LED_MODE_NONE);
+
+ case V4L2_CID_FLASH_STROBE_SOURCE:
+ return regmap_update_bits(flash->regmap,
+ REG_STROBE_SRC, MASK_STROBE_SRC,
+ (ctrl->val) << 7);
+
+ case V4L2_CID_FLASH_STROBE:
+
+ /* read and check current mode of chip to start flash */
+ rval = regmap_read(flash->regmap, REG_ENABLE, &reg_val);
+ if (rval < 0 || ((reg_val & MASK_ENABLE) != MODE_SHDN))
+ return rval;
+ /* flash on */
+ return lm3646_mode_ctrl(flash, V4L2_FLASH_LED_MODE_FLASH);
+
+ case V4L2_CID_FLASH_STROBE_STOP:
+
+ /*
+ * flash mode will be turned automatically
+ * from FLASH mode to SHDN mode after flash duration timeout
+ * read and check current mode of chip to stop flash
+ */
+ rval = regmap_read(flash->regmap, REG_ENABLE, &reg_val);
+ if (rval < 0)
+ return rval;
+ if ((reg_val & MASK_ENABLE) == MODE_FLASH)
+ return lm3646_mode_ctrl(flash,
+ V4L2_FLASH_LED_MODE_NONE);
+ return rval;
+
+ case V4L2_CID_FLASH_TIMEOUT:
+ return regmap_update_bits(flash->regmap,
+ REG_FLASH_TOUT, MASK_FLASH_TOUT,
+ LM3646_FLASH_TOUT_ms_TO_REG
+ (ctrl->val));
+
+ case V4L2_CID_FLASH_INTENSITY:
+ return regmap_update_bits(flash->regmap,
+ REG_FLASH_BR, MASK_FLASH_BR,
+ LM3646_TOTAL_FLASH_BRT_uA_TO_REG
+ (ctrl->val));
+
+ case V4L2_CID_FLASH_TORCH_INTENSITY:
+ return regmap_update_bits(flash->regmap,
+ REG_TORCH_BR, MASK_TORCH_BR,
+ LM3646_TOTAL_TORCH_BRT_uA_TO_REG
+ (ctrl->val) << 4);
+ }
+
+ return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops lm3646_led_ctrl_ops = {
+ .g_volatile_ctrl = lm3646_get_ctrl,
+ .s_ctrl = lm3646_set_ctrl,
+};
+
+static int lm3646_init_controls(struct lm3646_flash *flash)
+{
+ struct v4l2_ctrl *fault;
+ struct v4l2_ctrl_handler *hdl = &flash->ctrls_led;
+ const struct v4l2_ctrl_ops *ops = &lm3646_led_ctrl_ops;
+
+ v4l2_ctrl_handler_init(hdl, 8);
+ /* flash mode */
+ v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_LED_MODE,
+ V4L2_FLASH_LED_MODE_TORCH, ~0x7,
+ V4L2_FLASH_LED_MODE_NONE);
+
+ /* flash source */
+ v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_STROBE_SOURCE,
+ 0x1, ~0x3, V4L2_FLASH_STROBE_SOURCE_SOFTWARE);
+
+ /* flash strobe */
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_STROBE, 0, 0, 0, 0);
+ /* flash strobe stop */
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0);
+
+ /* flash strobe timeout */
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TIMEOUT,
+ LM3646_FLASH_TOUT_MIN,
+ LM3646_FLASH_TOUT_MAX,
+ LM3646_FLASH_TOUT_STEP, flash->pdata->flash_timeout);
+
+ /* max flash current */
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_INTENSITY,
+ LM3646_TOTAL_FLASH_BRT_MIN,
+ LM3646_TOTAL_FLASH_BRT_MAX,
+ LM3646_TOTAL_FLASH_BRT_STEP,
+ LM3646_TOTAL_FLASH_BRT_MAX);
+
+ /* max torch current */
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TORCH_INTENSITY,
+ LM3646_TOTAL_TORCH_BRT_MIN,
+ LM3646_TOTAL_TORCH_BRT_MAX,
+ LM3646_TOTAL_TORCH_BRT_STEP,
+ LM3646_TOTAL_TORCH_BRT_MAX);
+
+ /* fault */
+ fault = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_FAULT, 0,
+ V4L2_FLASH_FAULT_OVER_VOLTAGE
+ | V4L2_FLASH_FAULT_OVER_TEMPERATURE
+ | V4L2_FLASH_FAULT_SHORT_CIRCUIT
+ | V4L2_FLASH_FAULT_TIMEOUT, 0, 0);
+ if (fault != NULL)
+ fault->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ if (hdl->error)
+ return hdl->error;
+
+ flash->subdev_led.ctrl_handler = hdl;
+ return 0;
+}
+
+/* initialize device */
+static const struct v4l2_subdev_ops lm3646_ops = {
+ .core = NULL,
+};
+
+static const struct regmap_config lm3646_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xFF,
+};
+
+static int lm3646_subdev_init(struct lm3646_flash *flash)
+{
+ struct i2c_client *client = to_i2c_client(flash->dev);
+ int rval;
+
+ v4l2_i2c_subdev_init(&flash->subdev_led, client, &lm3646_ops);
+ flash->subdev_led.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ strcpy(flash->subdev_led.name, LM3646_NAME);
+ rval = lm3646_init_controls(flash);
+ if (rval)
+ goto err_out;
+ rval = media_entity_init(&flash->subdev_led.entity, 0, NULL, 0);
+ if (rval < 0)
+ goto err_out;
+ flash->subdev_led.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
+ return rval;
+
+err_out:
+ v4l2_ctrl_handler_free(&flash->ctrls_led);
+ return rval;
+}
+
+static int lm3646_init_device(struct lm3646_flash *flash)
+{
+ unsigned int reg_val;
+ int rval;
+
+ /* read the value of mode register to reduce redundant i2c accesses */
+ rval = regmap_read(flash->regmap, REG_ENABLE, &reg_val);
+ if (rval < 0)
+ return rval;
+ flash->mode_reg = reg_val & 0xfc;
+
+ /* output disable */
+ rval = lm3646_mode_ctrl(flash, V4L2_FLASH_LED_MODE_NONE);
+ if (rval < 0)
+ return rval;
+
+ /*
+ * LED1 flash current setting
+ * LED2 flash current = Total(Max) flash current - LED1 flash current
+ */
+ rval = regmap_update_bits(flash->regmap,
+ REG_LED1_FLASH_BR, 0x7F,
+ LM3646_LED1_FLASH_BRT_uA_TO_REG
+ (flash->pdata->led1_flash_brt));
+
+ if (rval < 0)
+ return rval;
+
+ /*
+ * LED1 torch current setting
+ * LED2 torch current = Total(Max) torch current - LED1 torch current
+ */
+ rval = regmap_update_bits(flash->regmap,
+ REG_LED1_TORCH_BR, 0x7F,
+ LM3646_LED1_TORCH_BRT_uA_TO_REG
+ (flash->pdata->led1_torch_brt));
+ if (rval < 0)
+ return rval;
+
+ /* Reset flag register */
+ return regmap_read(flash->regmap, REG_FLAG, &reg_val);
+}
+
+static int lm3646_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct lm3646_flash *flash;
+ struct lm3646_platform_data *pdata = dev_get_platdata(&client->dev);
+ int rval;
+
+ flash = devm_kzalloc(&client->dev, sizeof(*flash), GFP_KERNEL);
+ if (flash == NULL)
+ return -ENOMEM;
+
+ flash->regmap = devm_regmap_init_i2c(client, &lm3646_regmap);
+ if (IS_ERR(flash->regmap))
+ return PTR_ERR(flash->regmap);
+
+ /* check device tree if there is no platform data */
+ if (pdata == NULL) {
+ pdata = devm_kzalloc(&client->dev,
+ sizeof(struct lm3646_platform_data),
+ GFP_KERNEL);
+ if (pdata == NULL)
+ return -ENOMEM;
+ /* use default data in case of no platform data */
+ pdata->flash_timeout = LM3646_FLASH_TOUT_MAX;
+ pdata->led1_torch_brt = LM3646_LED1_TORCH_BRT_MAX;
+ pdata->led1_flash_brt = LM3646_LED1_FLASH_BRT_MAX;
+ }
+ flash->pdata = pdata;
+ flash->dev = &client->dev;
+
+ rval = lm3646_subdev_init(flash);
+ if (rval < 0)
+ return rval;
+
+ rval = lm3646_init_device(flash);
+ if (rval < 0)
+ return rval;
+
+ i2c_set_clientdata(client, flash);
+
+ return 0;
+}
+
+static int lm3646_remove(struct i2c_client *client)
+{
+ struct lm3646_flash *flash = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(&flash->subdev_led);
+ v4l2_ctrl_handler_free(&flash->ctrls_led);
+ media_entity_cleanup(&flash->subdev_led.entity);
+
+ return 0;
+}
+
+static const struct i2c_device_id lm3646_id_table[] = {
+ {LM3646_NAME, 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, lm3646_id_table);
+
+static struct i2c_driver lm3646_i2c_driver = {
+ .driver = {
+ .name = LM3646_NAME,
+ },
+ .probe = lm3646_probe,
+ .remove = lm3646_remove,
+ .id_table = lm3646_id_table,
+};
+
+module_i2c_driver(lm3646_i2c_driver);
+
+MODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>");
+MODULE_AUTHOR("Ldd Mlp <ldd-mlp@list.ti.com>");
+MODULE_DESCRIPTION("Texas Instruments LM3646 Dual Flash LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/m5mols/m5mols_capture.c b/drivers/media/i2c/m5mols/m5mols_capture.c
index ab34ccedf31..1a03d02bd4d 100644
--- a/drivers/media/i2c/m5mols/m5mols_capture.c
+++ b/drivers/media/i2c/m5mols/m5mols_capture.c
@@ -26,7 +26,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
#include <media/m5mols.h>
-#include <media/s5p_fimc.h>
+#include <media/exynos-fimc.h>
#include "m5mols.h"
#include "m5mols_reg.h"
diff --git a/drivers/media/i2c/ml86v7667.c b/drivers/media/i2c/ml86v7667.c
index a9110d8bbbc..2cace7313a2 100644
--- a/drivers/media/i2c/ml86v7667.c
+++ b/drivers/media/i2c/ml86v7667.c
@@ -276,6 +276,7 @@ static const struct v4l2_ctrl_ops ml86v7667_ctrl_ops = {
};
static struct v4l2_subdev_video_ops ml86v7667_subdev_video_ops = {
+ .s_std = ml86v7667_s_std,
.querystd = ml86v7667_querystd,
.g_input_status = ml86v7667_g_input_status,
.enum_mbus_fmt = ml86v7667_enum_mbus_fmt,
@@ -286,7 +287,6 @@ static struct v4l2_subdev_video_ops ml86v7667_subdev_video_ops = {
};
static struct v4l2_subdev_core_ops ml86v7667_subdev_core_ops = {
- .s_std = ml86v7667_s_std,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ml86v7667_g_register,
.s_register = ml86v7667_s_register,
diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c
index 8190fec6808..4d9c6bc3426 100644
--- a/drivers/media/i2c/msp3400-driver.c
+++ b/drivers/media/i2c/msp3400-driver.c
@@ -649,10 +649,10 @@ static const struct v4l2_subdev_core_ops msp_core_ops = {
.s_ctrl = v4l2_subdev_s_ctrl,
.queryctrl = v4l2_subdev_queryctrl,
.querymenu = v4l2_subdev_querymenu,
- .s_std = msp_s_std,
};
static const struct v4l2_subdev_video_ops msp_video_ops = {
+ .s_std = msp_s_std,
.querystd = msp_querystd,
};
diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
index e5ddf47030f..e18797ff7fa 100644
--- a/drivers/media/i2c/mt9p031.c
+++ b/drivers/media/i2c/mt9p031.c
@@ -21,6 +21,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
+#include <linux/of_graph.h>
#include <linux/pm.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
@@ -29,7 +30,6 @@
#include <media/mt9p031.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-of.h>
#include <media/v4l2-subdev.h>
#include "aptina-pll.h"
@@ -78,6 +78,9 @@
#define MT9P031_PLL_CONFIG_1 0x11
#define MT9P031_PLL_CONFIG_2 0x12
#define MT9P031_PIXEL_CLOCK_CONTROL 0x0a
+#define MT9P031_PIXEL_CLOCK_INVERT (1 << 15)
+#define MT9P031_PIXEL_CLOCK_SHIFT(n) ((n) << 8)
+#define MT9P031_PIXEL_CLOCK_DIVIDE(n) ((n) << 0)
#define MT9P031_FRAME_RESTART 0x0b
#define MT9P031_SHUTTER_DELAY 0x0c
#define MT9P031_RST 0x0d
@@ -130,6 +133,8 @@ struct mt9p031 {
enum mt9p031_model model;
struct aptina_pll pll;
+ unsigned int clk_div;
+ bool use_pll;
int reset;
struct v4l2_ctrl_handler ctrls;
@@ -198,6 +203,11 @@ static int mt9p031_reset(struct mt9p031 *mt9p031)
if (ret < 0)
return ret;
+ ret = mt9p031_write(client, MT9P031_PIXEL_CLOCK_CONTROL,
+ MT9P031_PIXEL_CLOCK_DIVIDE(mt9p031->clk_div));
+ if (ret < 0)
+ return ret;
+
return mt9p031_set_output_control(mt9p031, MT9P031_OUTPUT_CONTROL_CEN,
0);
}
@@ -222,15 +232,34 @@ static int mt9p031_clk_setup(struct mt9p031 *mt9p031)
struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
struct mt9p031_platform_data *pdata = mt9p031->pdata;
+ int ret;
mt9p031->clk = devm_clk_get(&client->dev, NULL);
if (IS_ERR(mt9p031->clk))
return PTR_ERR(mt9p031->clk);
- clk_set_rate(mt9p031->clk, pdata->ext_freq);
+ ret = clk_set_rate(mt9p031->clk, pdata->ext_freq);
+ if (ret < 0)
+ return ret;
+
+ /* If the external clock frequency is out of bounds for the PLL use the
+ * pixel clock divider only and disable the PLL.
+ */
+ if (pdata->ext_freq > limits.ext_clock_max) {
+ unsigned int div;
+
+ div = DIV_ROUND_UP(pdata->ext_freq, pdata->target_freq);
+ div = roundup_pow_of_two(div) / 2;
+
+ mt9p031->clk_div = max_t(unsigned int, div, 64);
+ mt9p031->use_pll = false;
+
+ return 0;
+ }
mt9p031->pll.ext_clock = pdata->ext_freq;
mt9p031->pll.pix_clock = pdata->target_freq;
+ mt9p031->use_pll = true;
return aptina_pll_calculate(&client->dev, &limits, &mt9p031->pll);
}
@@ -240,6 +269,9 @@ static int mt9p031_pll_enable(struct mt9p031 *mt9p031)
struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
int ret;
+ if (!mt9p031->use_pll)
+ return 0;
+
ret = mt9p031_write(client, MT9P031_PLL_CONTROL,
MT9P031_PLL_CONTROL_PWRON);
if (ret < 0)
@@ -265,6 +297,9 @@ static inline int mt9p031_pll_disable(struct mt9p031 *mt9p031)
{
struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ if (!mt9p031->use_pll)
+ return 0;
+
return mt9p031_write(client, MT9P031_PLL_CONTROL,
MT9P031_PLL_CONTROL_PWROFF);
}
@@ -285,9 +320,15 @@ static int mt9p031_power_on(struct mt9p031 *mt9p031)
if (ret < 0)
return ret;
- /* Emable clock */
- if (mt9p031->clk)
- clk_prepare_enable(mt9p031->clk);
+ /* Enable clock */
+ if (mt9p031->clk) {
+ ret = clk_prepare_enable(mt9p031->clk);
+ if (ret) {
+ regulator_bulk_disable(ARRAY_SIZE(mt9p031->regulators),
+ mt9p031->regulators);
+ return ret;
+ }
+ }
/* Now RESET_BAR must be high */
if (gpio_is_valid(mt9p031->reset)) {
@@ -606,6 +647,28 @@ static int mt9p031_set_crop(struct v4l2_subdev *subdev,
#define V4L2_CID_BLC_ANALOG_OFFSET (V4L2_CID_USER_BASE | 0x1004)
#define V4L2_CID_BLC_DIGITAL_OFFSET (V4L2_CID_USER_BASE | 0x1005)
+static int mt9p031_restore_blc(struct mt9p031 *mt9p031)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ int ret;
+
+ if (mt9p031->blc_auto->cur.val != 0) {
+ ret = mt9p031_set_mode2(mt9p031, 0,
+ MT9P031_READ_MODE_2_ROW_BLC);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (mt9p031->blc_offset->cur.val != 0) {
+ ret = mt9p031_write(client, MT9P031_ROW_BLACK_TARGET,
+ mt9p031->blc_offset->cur.val);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct mt9p031 *mt9p031 =
@@ -614,6 +677,9 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
u16 data;
int ret;
+ if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
+ return 0;
+
switch (ctrl->id) {
case V4L2_CID_EXPOSURE:
ret = mt9p031_write(client, MT9P031_SHUTTER_WIDTH_UPPER,
@@ -668,18 +734,20 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
MT9P031_READ_MODE_2_ROW_MIR, 0);
case V4L2_CID_TEST_PATTERN:
+ /* The digital side of the Black Level Calibration function must
+ * be disabled when generating a test pattern to avoid artifacts
+ * in the image. Activate (deactivate) the BLC-related controls
+ * when the test pattern is enabled (disabled).
+ */
+ v4l2_ctrl_activate(mt9p031->blc_auto, ctrl->val == 0);
+ v4l2_ctrl_activate(mt9p031->blc_offset, ctrl->val == 0);
+
if (!ctrl->val) {
- /* Restore the black level compensation settings. */
- if (mt9p031->blc_auto->cur.val != 0) {
- ret = mt9p031_s_ctrl(mt9p031->blc_auto);
- if (ret < 0)
- return ret;
- }
- if (mt9p031->blc_offset->cur.val != 0) {
- ret = mt9p031_s_ctrl(mt9p031->blc_offset);
- if (ret < 0)
- return ret;
- }
+ /* Restore the BLC settings. */
+ ret = mt9p031_restore_blc(mt9p031);
+ if (ret < 0)
+ return ret;
+
return mt9p031_write(client, MT9P031_TEST_PATTERN,
MT9P031_TEST_PATTERN_DISABLE);
}
@@ -694,9 +762,7 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
if (ret < 0)
return ret;
- /* Disable digital black level compensation when using a test
- * pattern.
- */
+ /* Disable digital BLC when generating a test pattern. */
ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC,
0);
if (ret < 0)
@@ -943,7 +1009,7 @@ mt9p031_get_pdata(struct i2c_client *client)
if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node)
return client->dev.platform_data;
- np = v4l2_of_get_next_endpoint(client->dev.of_node, NULL);
+ np = of_graph_get_next_endpoint(client->dev.of_node, NULL);
if (!np)
return NULL;
diff --git a/drivers/media/i2c/mt9t001.c b/drivers/media/i2c/mt9t001.c
index d41c70eaf83..422e068f5f1 100644
--- a/drivers/media/i2c/mt9t001.c
+++ b/drivers/media/i2c/mt9t001.c
@@ -12,9 +12,11 @@
* published by the Free Software Foundation.
*/
+#include <linux/clk.h>
#include <linux/i2c.h>
-#include <linux/module.h>
#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <linux/v4l2-mediabus.h>
@@ -55,6 +57,7 @@
#define MT9T001_OUTPUT_CONTROL_SYNC (1 << 0)
#define MT9T001_OUTPUT_CONTROL_CHIP_ENABLE (1 << 1)
#define MT9T001_OUTPUT_CONTROL_TEST_DATA (1 << 6)
+#define MT9T001_OUTPUT_CONTROL_DEF 0x0002
#define MT9T001_SHUTTER_WIDTH_HIGH 0x08
#define MT9T001_SHUTTER_WIDTH_LOW 0x09
#define MT9T001_SHUTTER_WIDTH_MIN 1
@@ -116,6 +119,12 @@ struct mt9t001 {
struct v4l2_subdev subdev;
struct media_pad pad;
+ struct clk *clk;
+ struct regulator_bulk_data regulators[2];
+
+ struct mutex power_lock; /* lock to protect power_count */
+ int power_count;
+
struct v4l2_mbus_framefmt format;
struct v4l2_rect crop;
@@ -159,6 +168,77 @@ static int mt9t001_set_output_control(struct mt9t001 *mt9t001, u16 clear,
return 0;
}
+static int mt9t001_reset(struct mt9t001 *mt9t001)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev);
+ int ret;
+
+ /* Reset the chip and stop data read out */
+ ret = mt9t001_write(client, MT9T001_RESET, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_RESET, 0);
+ if (ret < 0)
+ return ret;
+
+ mt9t001->output_control = MT9T001_OUTPUT_CONTROL_DEF;
+
+ return mt9t001_set_output_control(mt9t001,
+ MT9T001_OUTPUT_CONTROL_CHIP_ENABLE,
+ 0);
+}
+
+static int mt9t001_power_on(struct mt9t001 *mt9t001)
+{
+ int ret;
+
+ /* Bring up the supplies */
+ ret = regulator_bulk_enable(ARRAY_SIZE(mt9t001->regulators),
+ mt9t001->regulators);
+ if (ret < 0)
+ return ret;
+
+ /* Enable clock */
+ ret = clk_prepare_enable(mt9t001->clk);
+ if (ret < 0)
+ regulator_bulk_disable(ARRAY_SIZE(mt9t001->regulators),
+ mt9t001->regulators);
+
+ return ret;
+}
+
+static void mt9t001_power_off(struct mt9t001 *mt9t001)
+{
+ regulator_bulk_disable(ARRAY_SIZE(mt9t001->regulators),
+ mt9t001->regulators);
+
+ clk_disable_unprepare(mt9t001->clk);
+}
+
+static int __mt9t001_set_power(struct mt9t001 *mt9t001, bool on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev);
+ int ret;
+
+ if (!on) {
+ mt9t001_power_off(mt9t001);
+ return 0;
+ }
+
+ ret = mt9t001_power_on(mt9t001);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_reset(mt9t001);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to reset the camera\n");
+ return ret;
+ }
+
+ return v4l2_ctrl_handler_setup(&mt9t001->ctrls);
+}
+
/* -----------------------------------------------------------------------------
* V4L2 subdev video operations
*/
@@ -195,6 +275,7 @@ static int mt9t001_s_stream(struct v4l2_subdev *subdev, int enable)
{
const u16 mode = MT9T001_OUTPUT_CONTROL_CHIP_ENABLE;
struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct mt9t001_platform_data *pdata = client->dev.platform_data;
struct mt9t001 *mt9t001 = to_mt9t001(subdev);
struct v4l2_mbus_framefmt *format = &mt9t001->format;
struct v4l2_rect *crop = &mt9t001->crop;
@@ -205,6 +286,14 @@ static int mt9t001_s_stream(struct v4l2_subdev *subdev, int enable)
if (!enable)
return mt9t001_set_output_control(mt9t001, mode, 0);
+ /* Configure the pixel clock polarity */
+ if (pdata->clk_pol) {
+ ret = mt9t001_write(client, MT9T001_PIXEL_CLOCK,
+ MT9T001_PIXEL_CLOCK_INVERT);
+ if (ret < 0)
+ return ret;
+ }
+
/* Configure the window size and row/column bin */
hratio = DIV_ROUND_CLOSEST(crop->width, format->width);
vratio = DIV_ROUND_CLOSEST(crop->height, format->height);
@@ -630,9 +719,67 @@ static const struct v4l2_ctrl_config mt9t001_gains[] = {
};
/* -----------------------------------------------------------------------------
+ * V4L2 subdev core operations
+ */
+
+static int mt9t001_set_power(struct v4l2_subdev *subdev, int on)
+{
+ struct mt9t001 *mt9t001 = to_mt9t001(subdev);
+ int ret = 0;
+
+ mutex_lock(&mt9t001->power_lock);
+
+ /* If the power count is modified from 0 to != 0 or from != 0 to 0,
+ * update the power state.
+ */
+ if (mt9t001->power_count == !on) {
+ ret = __mt9t001_set_power(mt9t001, !!on);
+ if (ret < 0)
+ goto out;
+ }
+
+ /* Update the power count. */
+ mt9t001->power_count += on ? 1 : -1;
+ WARN_ON(mt9t001->power_count < 0);
+
+out:
+ mutex_unlock(&mt9t001->power_lock);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
* V4L2 subdev internal operations
*/
+static int mt9t001_registered(struct v4l2_subdev *subdev)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct mt9t001 *mt9t001 = to_mt9t001(subdev);
+ s32 data;
+ int ret;
+
+ ret = mt9t001_power_on(mt9t001);
+ if (ret < 0) {
+ dev_err(&client->dev, "MT9T001 power up failed\n");
+ return ret;
+ }
+
+ /* Read out the chip version register */
+ data = mt9t001_read(client, MT9T001_CHIP_VERSION);
+ mt9t001_power_off(mt9t001);
+
+ if (data != MT9T001_CHIP_ID) {
+ dev_err(&client->dev,
+ "MT9T001 not detected, wrong version 0x%04x\n", data);
+ return -ENODEV;
+ }
+
+ dev_info(&client->dev, "MT9T001 detected at address 0x%02x\n",
+ client->addr);
+
+ return 0;
+}
+
static int mt9t001_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
{
struct v4l2_mbus_framefmt *format;
@@ -651,9 +798,18 @@ static int mt9t001_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
format->field = V4L2_FIELD_NONE;
format->colorspace = V4L2_COLORSPACE_SRGB;
- return 0;
+ return mt9t001_set_power(subdev, 1);
+}
+
+static int mt9t001_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+ return mt9t001_set_power(subdev, 0);
}
+static struct v4l2_subdev_core_ops mt9t001_subdev_core_ops = {
+ .s_power = mt9t001_set_power,
+};
+
static struct v4l2_subdev_video_ops mt9t001_subdev_video_ops = {
.s_stream = mt9t001_s_stream,
};
@@ -668,58 +824,17 @@ static struct v4l2_subdev_pad_ops mt9t001_subdev_pad_ops = {
};
static struct v4l2_subdev_ops mt9t001_subdev_ops = {
+ .core = &mt9t001_subdev_core_ops,
.video = &mt9t001_subdev_video_ops,
.pad = &mt9t001_subdev_pad_ops,
};
static struct v4l2_subdev_internal_ops mt9t001_subdev_internal_ops = {
+ .registered = mt9t001_registered,
.open = mt9t001_open,
+ .close = mt9t001_close,
};
-static int mt9t001_video_probe(struct i2c_client *client)
-{
- struct mt9t001_platform_data *pdata = client->dev.platform_data;
- s32 data;
- int ret;
-
- dev_info(&client->dev, "Probing MT9T001 at address 0x%02x\n",
- client->addr);
-
- /* Reset the chip and stop data read out */
- ret = mt9t001_write(client, MT9T001_RESET, 1);
- if (ret < 0)
- return ret;
-
- ret = mt9t001_write(client, MT9T001_RESET, 0);
- if (ret < 0)
- return ret;
-
- ret = mt9t001_write(client, MT9T001_OUTPUT_CONTROL, 0);
- if (ret < 0)
- return ret;
-
- /* Configure the pixel clock polarity */
- if (pdata->clk_pol) {
- ret = mt9t001_write(client, MT9T001_PIXEL_CLOCK,
- MT9T001_PIXEL_CLOCK_INVERT);
- if (ret < 0)
- return ret;
- }
-
- /* Read and check the sensor version */
- data = mt9t001_read(client, MT9T001_CHIP_VERSION);
- if (data != MT9T001_CHIP_ID) {
- dev_err(&client->dev, "MT9T001 not detected, wrong version "
- "0x%04x\n", data);
- return -ENODEV;
- }
-
- dev_info(&client->dev, "MT9T001 detected at address 0x%02x\n",
- client->addr);
-
- return ret;
-}
-
static int mt9t001_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
@@ -740,14 +855,28 @@ static int mt9t001_probe(struct i2c_client *client,
return -EIO;
}
- ret = mt9t001_video_probe(client);
- if (ret < 0)
- return ret;
-
mt9t001 = devm_kzalloc(&client->dev, sizeof(*mt9t001), GFP_KERNEL);
if (!mt9t001)
return -ENOMEM;
+ mutex_init(&mt9t001->power_lock);
+ mt9t001->output_control = MT9T001_OUTPUT_CONTROL_DEF;
+
+ mt9t001->regulators[0].supply = "vdd";
+ mt9t001->regulators[1].supply = "vaa";
+
+ ret = devm_regulator_bulk_get(&client->dev, 2, mt9t001->regulators);
+ if (ret < 0) {
+ dev_err(&client->dev, "Unable to get regulators\n");
+ return ret;
+ }
+
+ mt9t001->clk = devm_clk_get(&client->dev, NULL);
+ if (IS_ERR(mt9t001->clk)) {
+ dev_err(&client->dev, "Unable to get clock\n");
+ return PTR_ERR(mt9t001->clk);
+ }
+
v4l2_ctrl_handler_init(&mt9t001->ctrls, ARRAY_SIZE(mt9t001_ctrls) +
ARRAY_SIZE(mt9t001_gains) + 4);
diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c
index f74698cf14c..47e475319a2 100644
--- a/drivers/media/i2c/mt9v011.c
+++ b/drivers/media/i2c/mt9v011.c
@@ -1,7 +1,7 @@
/*
* mt9v011 -Micron 1/4-Inch VGA Digital Image Sensor
*
- * Copyright (c) 2009 Mauro Carvalho Chehab (mchehab@redhat.com)
+ * Copyright (c) 2009 Mauro Carvalho Chehab
* This code is placed under the terms of the GNU General Public License v2
*/
@@ -16,7 +16,7 @@
#include <media/mt9v011.h>
MODULE_DESCRIPTION("Micron mt9v011 sensor driver");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_LICENSE("GPL");
static int debug;
diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c
index 36c504b78f2..40172b8d8ea 100644
--- a/drivers/media/i2c/mt9v032.c
+++ b/drivers/media/i2c/mt9v032.c
@@ -317,8 +317,14 @@ static int mt9v032_power_on(struct mt9v032 *mt9v032)
struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev);
int ret;
- clk_set_rate(mt9v032->clk, mt9v032->sysclk);
- clk_prepare_enable(mt9v032->clk);
+ ret = clk_set_rate(mt9v032->clk, mt9v032->sysclk);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_prepare_enable(mt9v032->clk);
+ if (ret)
+ return ret;
+
udelay(1);
/* Reset the chip and stop data read out */
diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c
index e8a1ce20403..cdd7c1b7259 100644
--- a/drivers/media/i2c/ov7670.c
+++ b/drivers/media/i2c/ov7670.c
@@ -1109,7 +1109,7 @@ static int ov7670_enum_framesizes(struct v4l2_subdev *sd,
* windows that fall outside that.
*/
for (i = 0; i < n_win_sizes; i++) {
- struct ov7670_win_size *win = &info->devtype->win_sizes[index];
+ struct ov7670_win_size *win = &info->devtype->win_sizes[i];
if (info->min_width && win->width < info->min_width)
continue;
if (info->min_height && win->height < info->min_height)
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
index e7f555cc827..ee0f57e01b5 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
@@ -15,7 +15,7 @@
* GNU General Public License for more details.
*/
-#include <linux/sizes.h>
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/gpio.h>
@@ -23,7 +23,9 @@
#include <linux/init.h>
#include <linux/media.h>
#include <linux/module.h>
+#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
+#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/videodev2.h>
@@ -33,6 +35,7 @@
#include <media/v4l2-subdev.h>
#include <media/v4l2-mediabus.h>
#include <media/s5c73m3.h>
+#include <media/v4l2-of.h>
#include "s5c73m3.h"
@@ -46,6 +49,8 @@ static int update_fw;
module_param(update_fw, int, 0644);
#define S5C73M3_EMBEDDED_DATA_MAXLEN SZ_4K
+#define S5C73M3_MIPI_DATA_LANES 4
+#define S5C73M3_CLK_NAME "cis_extclk"
static const char * const s5c73m3_supply_names[S5C73M3_MAX_SUPPLIES] = {
"vdd-int", /* Digital Core supply (1.2V), CAM_ISP_CORE_1.2V */
@@ -1355,9 +1360,20 @@ static int __s5c73m3_power_on(struct s5c73m3 *state)
for (i = 0; i < S5C73M3_MAX_SUPPLIES; i++) {
ret = regulator_enable(state->supplies[i].consumer);
if (ret)
- goto err;
+ goto err_reg_dis;
}
+ ret = clk_set_rate(state->clock, state->mclk_frequency);
+ if (ret < 0)
+ goto err_reg_dis;
+
+ ret = clk_prepare_enable(state->clock);
+ if (ret < 0)
+ goto err_reg_dis;
+
+ v4l2_dbg(1, s5c73m3_dbg, &state->oif_sd, "clock frequency: %ld\n",
+ clk_get_rate(state->clock));
+
s5c73m3_gpio_deassert(state, STBY);
usleep_range(100, 200);
@@ -1365,7 +1381,8 @@ static int __s5c73m3_power_on(struct s5c73m3 *state)
usleep_range(50, 100);
return 0;
-err:
+
+err_reg_dis:
for (--i; i >= 0; i--)
regulator_disable(state->supplies[i].consumer);
return ret;
@@ -1380,6 +1397,9 @@ static int __s5c73m3_power_off(struct s5c73m3 *state)
if (s5c73m3_gpio_assert(state, STBY))
usleep_range(100, 200);
+
+ clk_disable_unprepare(state->clock);
+
state->streaming = 0;
state->isp_ready = 0;
@@ -1388,6 +1408,7 @@ static int __s5c73m3_power_off(struct s5c73m3 *state)
if (ret)
goto err;
}
+
return 0;
err:
for (++i; i < S5C73M3_MAX_SUPPLIES; i++) {
@@ -1396,6 +1417,8 @@ err:
v4l2_err(&state->oif_sd, "Failed to reenable %s: %d\n",
state->supplies[i].supply, r);
}
+
+ clk_prepare_enable(state->clock);
return ret;
}
@@ -1451,17 +1474,6 @@ static int s5c73m3_oif_registered(struct v4l2_subdev *sd)
S5C73M3_JPEG_PAD, &state->oif_sd.entity, OIF_JPEG_PAD,
MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
- mutex_lock(&state->lock);
- ret = __s5c73m3_power_on(state);
- if (ret == 0)
- s5c73m3_get_fw_version(state);
-
- __s5c73m3_power_off(state);
- mutex_unlock(&state->lock);
-
- v4l2_dbg(1, s5c73m3_dbg, sd, "%s: Booting %s (%d)\n",
- __func__, ret ? "failed" : "succeeded", ret);
-
return ret;
}
@@ -1519,41 +1531,112 @@ static const struct v4l2_subdev_ops oif_subdev_ops = {
.video = &s5c73m3_oif_video_ops,
};
-static int s5c73m3_configure_gpios(struct s5c73m3 *state,
- const struct s5c73m3_platform_data *pdata)
+static int s5c73m3_configure_gpios(struct s5c73m3 *state)
+{
+ static const char * const gpio_names[] = {
+ "S5C73M3_STBY", "S5C73M3_RST"
+ };
+ struct i2c_client *c = state->i2c_client;
+ struct s5c73m3_gpio *g = state->gpio;
+ int ret, i;
+
+ for (i = 0; i < GPIO_NUM; ++i) {
+ unsigned int flags = GPIOF_DIR_OUT;
+ if (g[i].level)
+ flags |= GPIOF_INIT_HIGH;
+ ret = devm_gpio_request_one(&c->dev, g[i].gpio, flags,
+ gpio_names[i]);
+ if (ret) {
+ v4l2_err(c, "failed to request gpio %s\n",
+ gpio_names[i]);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int s5c73m3_parse_gpios(struct s5c73m3 *state)
+{
+ static const char * const prop_names[] = {
+ "standby-gpios", "xshutdown-gpios",
+ };
+ struct device *dev = &state->i2c_client->dev;
+ struct device_node *node = dev->of_node;
+ int ret, i;
+
+ for (i = 0; i < GPIO_NUM; ++i) {
+ enum of_gpio_flags of_flags;
+
+ ret = of_get_named_gpio_flags(node, prop_names[i],
+ 0, &of_flags);
+ if (ret < 0) {
+ dev_err(dev, "failed to parse %s DT property\n",
+ prop_names[i]);
+ return -EINVAL;
+ }
+ state->gpio[i].gpio = ret;
+ state->gpio[i].level = !(of_flags & OF_GPIO_ACTIVE_LOW);
+ }
+ return 0;
+}
+
+static int s5c73m3_get_platform_data(struct s5c73m3 *state)
{
struct device *dev = &state->i2c_client->dev;
- const struct s5c73m3_gpio *gpio;
- unsigned long flags;
+ const struct s5c73m3_platform_data *pdata = dev->platform_data;
+ struct device_node *node = dev->of_node;
+ struct device_node *node_ep;
+ struct v4l2_of_endpoint ep;
int ret;
- state->gpio[STBY].gpio = -EINVAL;
- state->gpio[RST].gpio = -EINVAL;
+ if (!node) {
+ if (!pdata) {
+ dev_err(dev, "Platform data not specified\n");
+ return -EINVAL;
+ }
- gpio = &pdata->gpio_stby;
- if (gpio_is_valid(gpio->gpio)) {
- flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW)
- | GPIOF_EXPORT;
- ret = devm_gpio_request_one(dev, gpio->gpio, flags,
- "S5C73M3_STBY");
- if (ret < 0)
- return ret;
+ state->mclk_frequency = pdata->mclk_frequency;
+ state->gpio[STBY] = pdata->gpio_stby;
+ state->gpio[RST] = pdata->gpio_reset;
+ return 0;
+ }
+
+ state->clock = devm_clk_get(dev, S5C73M3_CLK_NAME);
+ if (IS_ERR(state->clock))
+ return PTR_ERR(state->clock);
- state->gpio[STBY] = *gpio;
+ if (of_property_read_u32(node, "clock-frequency",
+ &state->mclk_frequency)) {
+ state->mclk_frequency = S5C73M3_DEFAULT_MCLK_FREQ;
+ dev_info(dev, "using default %u Hz clock frequency\n",
+ state->mclk_frequency);
}
- gpio = &pdata->gpio_reset;
- if (gpio_is_valid(gpio->gpio)) {
- flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW)
- | GPIOF_EXPORT;
- ret = devm_gpio_request_one(dev, gpio->gpio, flags,
- "S5C73M3_RST");
- if (ret < 0)
- return ret;
+ ret = s5c73m3_parse_gpios(state);
+ if (ret < 0)
+ return -EINVAL;
- state->gpio[RST] = *gpio;
+ node_ep = of_graph_get_next_endpoint(node, NULL);
+ if (!node_ep) {
+ dev_warn(dev, "no endpoint defined for node: %s\n",
+ node->full_name);
+ return 0;
}
+ v4l2_of_parse_endpoint(node_ep, &ep);
+ of_node_put(node_ep);
+
+ if (ep.bus_type != V4L2_MBUS_CSI2) {
+ dev_err(dev, "unsupported bus type\n");
+ return -EINVAL;
+ }
+ /*
+ * Number of MIPI CSI-2 data lanes is currently not configurable,
+ * always a default value of 4 lanes is used.
+ */
+ if (ep.bus.mipi_csi2.num_data_lanes != S5C73M3_MIPI_DATA_LANES)
+ dev_info(dev, "falling back to 4 MIPI CSI-2 data lanes\n");
+
return 0;
}
@@ -1561,21 +1644,20 @@ static int s5c73m3_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
- const struct s5c73m3_platform_data *pdata = client->dev.platform_data;
struct v4l2_subdev *sd;
struct v4l2_subdev *oif_sd;
struct s5c73m3 *state;
int ret, i;
- if (pdata == NULL) {
- dev_err(&client->dev, "Platform data not specified\n");
- return -EINVAL;
- }
-
state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
+ state->i2c_client = client;
+ ret = s5c73m3_get_platform_data(state);
+ if (ret < 0)
+ return ret;
+
mutex_init(&state->lock);
sd = &state->sensor_sd;
oif_sd = &state->oif_sd;
@@ -1613,11 +1695,7 @@ static int s5c73m3_probe(struct i2c_client *client,
if (ret < 0)
return ret;
- state->mclk_frequency = pdata->mclk_frequency;
- state->bus_type = pdata->bus_type;
- state->i2c_client = client;
-
- ret = s5c73m3_configure_gpios(state, pdata);
+ ret = s5c73m3_configure_gpios(state);
if (ret)
goto out_err;
@@ -1651,9 +1729,29 @@ static int s5c73m3_probe(struct i2c_client *client,
if (ret < 0)
goto out_err;
+ oif_sd->dev = dev;
+
+ ret = __s5c73m3_power_on(state);
+ if (ret < 0)
+ goto out_err1;
+
+ ret = s5c73m3_get_fw_version(state);
+ __s5c73m3_power_off(state);
+
+ if (ret < 0) {
+ dev_err(dev, "Device detection failed: %d\n", ret);
+ goto out_err1;
+ }
+
+ ret = v4l2_async_register_subdev(oif_sd);
+ if (ret < 0)
+ goto out_err1;
+
v4l2_info(sd, "%s: completed successfully\n", __func__);
return 0;
+out_err1:
+ s5c73m3_unregister_spi_driver(state);
out_err:
media_entity_cleanup(&sd->entity);
return ret;
@@ -1665,7 +1763,7 @@ static int s5c73m3_remove(struct i2c_client *client)
struct s5c73m3 *state = oif_sd_to_s5c73m3(oif_sd);
struct v4l2_subdev *sensor_sd = &state->sensor_sd;
- v4l2_device_unregister_subdev(oif_sd);
+ v4l2_async_unregister_subdev(oif_sd);
v4l2_ctrl_handler_free(oif_sd->ctrl_handler);
media_entity_cleanup(&oif_sd->entity);
@@ -1684,8 +1782,17 @@ static const struct i2c_device_id s5c73m3_id[] = {
};
MODULE_DEVICE_TABLE(i2c, s5c73m3_id);
+#ifdef CONFIG_OF
+static const struct of_device_id s5c73m3_of_match[] = {
+ { .compatible = "samsung,s5c73m3" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, s5c73m3_of_match);
+#endif
+
static struct i2c_driver s5c73m3_i2c_driver = {
.driver = {
+ .of_match_table = of_match_ptr(s5c73m3_of_match),
.name = DRIVER_NAME,
},
.probe = s5c73m3_probe,
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c
index 8079e26eb5e..f60b265b4da 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c
@@ -27,6 +27,11 @@
#define S5C73M3_SPI_DRV_NAME "S5C73M3-SPI"
+static const struct of_device_id s5c73m3_spi_ids[] = {
+ { .compatible = "samsung,s5c73m3" },
+ { }
+};
+
enum spi_direction {
SPI_DIR_RX,
SPI_DIR_TX
@@ -146,6 +151,7 @@ int s5c73m3_register_spi_driver(struct s5c73m3 *state)
spidrv->driver.name = S5C73M3_SPI_DRV_NAME;
spidrv->driver.bus = &spi_bus_type;
spidrv->driver.owner = THIS_MODULE;
+ spidrv->driver.of_match_table = s5c73m3_spi_ids;
return spi_register_driver(spidrv);
}
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3.h b/drivers/media/i2c/s5c73m3/s5c73m3.h
index 9dfa516f694..9656b6723dc 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3.h
+++ b/drivers/media/i2c/s5c73m3/s5c73m3.h
@@ -17,6 +17,7 @@
#ifndef S5C73M3_H_
#define S5C73M3_H_
+#include <linux/clk.h>
#include <linux/kernel.h>
#include <linux/regulator/consumer.h>
#include <media/v4l2-common.h>
@@ -321,6 +322,7 @@ enum s5c73m3_oif_pads {
#define S5C73M3_MAX_SUPPLIES 6
+#define S5C73M3_DEFAULT_MCLK_FREQ 24000000U
struct s5c73m3_ctrls {
struct v4l2_ctrl_handler handler;
@@ -391,6 +393,8 @@ struct s5c73m3 {
struct regulator_bulk_data supplies[S5C73M3_MAX_SUPPLIES];
struct s5c73m3_gpio gpio[GPIO_NUM];
+ struct clk *clock;
+
/* External master clock frequency */
u32 mclk_frequency;
/* Video bus type - MIPI-CSI2/parallel */
diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c
index 4b8381111cb..2d768ef67cc 100644
--- a/drivers/media/i2c/s5k5baf.c
+++ b/drivers/media/i2c/s5k5baf.c
@@ -21,6 +21,7 @@
#include <linux/media.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
+#include <linux/of_graph.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
@@ -478,25 +479,33 @@ static void s5k5baf_write_arr_seq(struct s5k5baf *state, u16 addr,
u16 count, const u16 *seq)
{
struct i2c_client *c = v4l2_get_subdevdata(&state->sd);
- __be16 buf[count + 1];
- int ret, n;
+ __be16 buf[65];
s5k5baf_i2c_write(state, REG_CMDWR_ADDR, addr);
if (state->error)
return;
+ v4l2_dbg(3, debug, c, "i2c_write_seq(count=%d): %*ph\n", count,
+ min(2 * count, 64), seq);
+
buf[0] = __constant_cpu_to_be16(REG_CMD_BUF);
- for (n = 1; n <= count; ++n)
- buf[n] = cpu_to_be16(*seq++);
- n *= 2;
- ret = i2c_master_send(c, (char *)buf, n);
- v4l2_dbg(3, debug, c, "i2c_write_seq(count=%d): %*ph\n", count,
- min(2 * count, 64), seq - count);
+ while (count > 0) {
+ int n = min_t(int, count, ARRAY_SIZE(buf) - 1);
+ int ret, i;
- if (ret != n) {
- v4l2_err(c, "i2c_write_seq: error during transfer (%d)\n", ret);
- state->error = ret;
+ for (i = 1; i <= n; ++i)
+ buf[i] = cpu_to_be16(*seq++);
+
+ i *= 2;
+ ret = i2c_master_send(c, (char *)buf, i);
+ if (ret != i) {
+ v4l2_err(c, "i2c_write_seq: error during transfer (%d)\n", ret);
+ state->error = ret;
+ break;
+ }
+
+ count -= n;
}
}
@@ -1847,7 +1856,7 @@ static int s5k5baf_parse_device_node(struct s5k5baf *state, struct device *dev)
if (ret < 0)
return ret;
- node_ep = v4l2_of_get_next_endpoint(node, NULL);
+ node_ep = of_graph_get_next_endpoint(node, NULL);
if (!node_ep) {
dev_err(dev, "no endpoint defined at node %s\n",
node->full_name);
diff --git a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c
new file mode 100644
index 00000000000..7bc2271ca00
--- /dev/null
+++ b/drivers/media/i2c/s5k6a3.c
@@ -0,0 +1,389 @@
+/*
+ * Samsung S5K6A3 image sensor driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-subdev.h>
+
+#define S5K6A3_SENSOR_MAX_WIDTH 1412
+#define S5K6A3_SENSOR_MAX_HEIGHT 1412
+#define S5K6A3_SENSOR_MIN_WIDTH 32
+#define S5K6A3_SENSOR_MIN_HEIGHT 32
+
+#define S5K6A3_DEFAULT_WIDTH 1296
+#define S5K6A3_DEFAULT_HEIGHT 732
+
+#define S5K6A3_DRV_NAME "S5K6A3"
+#define S5K6A3_CLK_NAME "extclk"
+#define S5K6A3_DEFAULT_CLK_FREQ 24000000U
+
+enum {
+ S5K6A3_SUPP_VDDA,
+ S5K6A3_SUPP_VDDIO,
+ S5K6A3_SUPP_AFVDD,
+ S5K6A3_NUM_SUPPLIES,
+};
+
+/**
+ * struct s5k6a3 - fimc-is sensor data structure
+ * @dev: pointer to this I2C client device structure
+ * @subdev: the image sensor's v4l2 subdev
+ * @pad: subdev media source pad
+ * @supplies: image sensor's voltage regulator supplies
+ * @gpio_reset: GPIO connected to the sensor's reset pin
+ * @lock: mutex protecting the structure's members below
+ * @format: media bus format at the sensor's source pad
+ */
+struct s5k6a3 {
+ struct device *dev;
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+ struct regulator_bulk_data supplies[S5K6A3_NUM_SUPPLIES];
+ int gpio_reset;
+ struct mutex lock;
+ struct v4l2_mbus_framefmt format;
+ struct clk *clock;
+ u32 clock_frequency;
+ int power_count;
+};
+
+static const char * const s5k6a3_supply_names[] = {
+ [S5K6A3_SUPP_VDDA] = "svdda",
+ [S5K6A3_SUPP_VDDIO] = "svddio",
+ [S5K6A3_SUPP_AFVDD] = "afvdd",
+};
+
+static inline struct s5k6a3 *sd_to_s5k6a3(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct s5k6a3, subdev);
+}
+
+static const struct v4l2_mbus_framefmt s5k6a3_formats[] = {
+ {
+ .code = V4L2_MBUS_FMT_SGRBG10_1X10,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .field = V4L2_FIELD_NONE,
+ }
+};
+
+static const struct v4l2_mbus_framefmt *find_sensor_format(
+ struct v4l2_mbus_framefmt *mf)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(s5k6a3_formats); i++)
+ if (mf->code == s5k6a3_formats[i].code)
+ return &s5k6a3_formats[i];
+
+ return &s5k6a3_formats[0];
+}
+
+static int s5k6a3_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index >= ARRAY_SIZE(s5k6a3_formats))
+ return -EINVAL;
+
+ code->code = s5k6a3_formats[code->index].code;
+ return 0;
+}
+
+static void s5k6a3_try_format(struct v4l2_mbus_framefmt *mf)
+{
+ const struct v4l2_mbus_framefmt *fmt;
+
+ fmt = find_sensor_format(mf);
+ mf->code = fmt->code;
+ v4l_bound_align_image(&mf->width, S5K6A3_SENSOR_MIN_WIDTH,
+ S5K6A3_SENSOR_MAX_WIDTH, 0,
+ &mf->height, S5K6A3_SENSOR_MIN_HEIGHT,
+ S5K6A3_SENSOR_MAX_HEIGHT, 0, 0);
+}
+
+static struct v4l2_mbus_framefmt *__s5k6a3_get_format(
+ struct s5k6a3 *sensor, struct v4l2_subdev_fh *fh,
+ u32 pad, enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL;
+
+ return &sensor->format;
+}
+
+static int s5k6a3_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct s5k6a3 *sensor = sd_to_s5k6a3(sd);
+ struct v4l2_mbus_framefmt *mf;
+
+ s5k6a3_try_format(&fmt->format);
+
+ mf = __s5k6a3_get_format(sensor, fh, fmt->pad, fmt->which);
+ if (mf) {
+ mutex_lock(&sensor->lock);
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ *mf = fmt->format;
+ mutex_unlock(&sensor->lock);
+ }
+ return 0;
+}
+
+static int s5k6a3_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct s5k6a3 *sensor = sd_to_s5k6a3(sd);
+ struct v4l2_mbus_framefmt *mf;
+
+ mf = __s5k6a3_get_format(sensor, fh, fmt->pad, fmt->which);
+
+ mutex_lock(&sensor->lock);
+ fmt->format = *mf;
+ mutex_unlock(&sensor->lock);
+ return 0;
+}
+
+static struct v4l2_subdev_pad_ops s5k6a3_pad_ops = {
+ .enum_mbus_code = s5k6a3_enum_mbus_code,
+ .get_fmt = s5k6a3_get_fmt,
+ .set_fmt = s5k6a3_set_fmt,
+};
+
+static int s5k6a3_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
+
+ *format = s5k6a3_formats[0];
+ format->width = S5K6A3_DEFAULT_WIDTH;
+ format->height = S5K6A3_DEFAULT_HEIGHT;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops s5k6a3_sd_internal_ops = {
+ .open = s5k6a3_open,
+};
+
+static int __s5k6a3_power_on(struct s5k6a3 *sensor)
+{
+ int i = S5K6A3_SUPP_VDDA;
+ int ret;
+
+ ret = clk_set_rate(sensor->clock, sensor->clock_frequency);
+ if (ret < 0)
+ return ret;
+
+ ret = pm_runtime_get(sensor->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = regulator_enable(sensor->supplies[i].consumer);
+ if (ret < 0)
+ goto error_rpm_put;
+
+ ret = clk_prepare_enable(sensor->clock);
+ if (ret < 0)
+ goto error_reg_dis;
+
+ for (i++; i < S5K6A3_NUM_SUPPLIES; i++) {
+ ret = regulator_enable(sensor->supplies[i].consumer);
+ if (ret < 0)
+ goto error_reg_dis;
+ }
+
+ gpio_set_value(sensor->gpio_reset, 1);
+ usleep_range(600, 800);
+ gpio_set_value(sensor->gpio_reset, 0);
+ usleep_range(600, 800);
+ gpio_set_value(sensor->gpio_reset, 1);
+
+ /* Delay needed for the sensor initialization */
+ msleep(20);
+ return 0;
+
+error_reg_dis:
+ for (--i; i >= 0; --i)
+ regulator_disable(sensor->supplies[i].consumer);
+error_rpm_put:
+ pm_runtime_put(sensor->dev);
+ return ret;
+}
+
+static int __s5k6a3_power_off(struct s5k6a3 *sensor)
+{
+ int i;
+
+ gpio_set_value(sensor->gpio_reset, 0);
+
+ for (i = S5K6A3_NUM_SUPPLIES - 1; i >= 0; i--)
+ regulator_disable(sensor->supplies[i].consumer);
+
+ clk_disable_unprepare(sensor->clock);
+ pm_runtime_put(sensor->dev);
+ return 0;
+}
+
+static int s5k6a3_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct s5k6a3 *sensor = sd_to_s5k6a3(sd);
+ int ret = 0;
+
+ mutex_lock(&sensor->lock);
+
+ if (sensor->power_count == !on) {
+ if (on)
+ ret = __s5k6a3_power_on(sensor);
+ else
+ ret = __s5k6a3_power_off(sensor);
+
+ if (ret == 0)
+ sensor->power_count += on ? 1 : -1;
+ }
+
+ mutex_unlock(&sensor->lock);
+ return ret;
+}
+
+static struct v4l2_subdev_core_ops s5k6a3_core_ops = {
+ .s_power = s5k6a3_s_power,
+};
+
+static struct v4l2_subdev_ops s5k6a3_subdev_ops = {
+ .core = &s5k6a3_core_ops,
+ .pad = &s5k6a3_pad_ops,
+};
+
+static int s5k6a3_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct s5k6a3 *sensor;
+ struct v4l2_subdev *sd;
+ int gpio, i, ret;
+
+ sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
+ if (!sensor)
+ return -ENOMEM;
+
+ mutex_init(&sensor->lock);
+ sensor->gpio_reset = -EINVAL;
+ sensor->clock = ERR_PTR(-EINVAL);
+ sensor->dev = dev;
+
+ sensor->clock = devm_clk_get(sensor->dev, S5K6A3_CLK_NAME);
+ if (IS_ERR(sensor->clock))
+ return PTR_ERR(sensor->clock);
+
+ gpio = of_get_gpio_flags(dev->of_node, 0, NULL);
+ if (!gpio_is_valid(gpio))
+ return gpio;
+
+ ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_LOW,
+ S5K6A3_DRV_NAME);
+ if (ret < 0)
+ return ret;
+
+ sensor->gpio_reset = gpio;
+
+ if (of_property_read_u32(dev->of_node, "clock-frequency",
+ &sensor->clock_frequency)) {
+ sensor->clock_frequency = S5K6A3_DEFAULT_CLK_FREQ;
+ dev_info(dev, "using default %u Hz clock frequency\n",
+ sensor->clock_frequency);
+ }
+
+ for (i = 0; i < S5K6A3_NUM_SUPPLIES; i++)
+ sensor->supplies[i].supply = s5k6a3_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&client->dev, S5K6A3_NUM_SUPPLIES,
+ sensor->supplies);
+ if (ret < 0)
+ return ret;
+
+ sd = &sensor->subdev;
+ v4l2_i2c_subdev_init(sd, client, &s5k6a3_subdev_ops);
+ sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sd->internal_ops = &s5k6a3_sd_internal_ops;
+
+ sensor->format.code = s5k6a3_formats[0].code;
+ sensor->format.width = S5K6A3_DEFAULT_WIDTH;
+ sensor->format.height = S5K6A3_DEFAULT_HEIGHT;
+
+ sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&sd->entity, 1, &sensor->pad, 0);
+ if (ret < 0)
+ return ret;
+
+ pm_runtime_no_callbacks(dev);
+ pm_runtime_enable(dev);
+
+ ret = v4l2_async_register_subdev(sd);
+
+ if (ret < 0) {
+ pm_runtime_disable(&client->dev);
+ media_entity_cleanup(&sd->entity);
+ }
+
+ return ret;
+}
+
+static int s5k6a3_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ pm_runtime_disable(&client->dev);
+ v4l2_async_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ return 0;
+}
+
+static const struct i2c_device_id s5k6a3_ids[] = {
+ { }
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id s5k6a3_of_match[] = {
+ { .compatible = "samsung,s5k6a3" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, s5k6a3_of_match);
+#endif
+
+static struct i2c_driver s5k6a3_driver = {
+ .driver = {
+ .of_match_table = of_match_ptr(s5k6a3_of_match),
+ .name = S5K6A3_DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = s5k6a3_probe,
+ .remove = s5k6a3_remove,
+ .id_table = s5k6a3_ids,
+};
+
+module_i2c_driver(s5k6a3_driver);
+
+MODULE_DESCRIPTION("S5K6A3 image sensor subdev driver");
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/saa6752hs.c b/drivers/media/i2c/saa6752hs.c
index 8272c0b9c5b..04e9e55018a 100644
--- a/drivers/media/i2c/saa6752hs.c
+++ b/drivers/media/i2c/saa6752hs.c
@@ -643,10 +643,10 @@ static const struct v4l2_ctrl_ops saa6752hs_ctrl_ops = {
static const struct v4l2_subdev_core_ops saa6752hs_core_ops = {
.init = saa6752hs_init,
- .s_std = saa6752hs_s_std,
};
static const struct v4l2_subdev_video_ops saa6752hs_video_ops = {
+ .s_std = saa6752hs_s_std,
.s_mbus_fmt = saa6752hs_s_mbus_fmt,
.try_mbus_fmt = saa6752hs_try_mbus_fmt,
.g_mbus_fmt = saa6752hs_g_mbus_fmt,
diff --git a/drivers/media/i2c/saa7110.c b/drivers/media/i2c/saa7110.c
index ac43e929a1d..99689ee57d7 100644
--- a/drivers/media/i2c/saa7110.c
+++ b/drivers/media/i2c/saa7110.c
@@ -365,10 +365,10 @@ static const struct v4l2_subdev_core_ops saa7110_core_ops = {
.s_ctrl = v4l2_subdev_s_ctrl,
.queryctrl = v4l2_subdev_queryctrl,
.querymenu = v4l2_subdev_querymenu,
- .s_std = saa7110_s_std,
};
static const struct v4l2_subdev_video_ops saa7110_video_ops = {
+ .s_std = saa7110_s_std,
.s_routing = saa7110_s_routing,
.s_stream = saa7110_s_stream,
.querystd = saa7110_querystd,
diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c
index afdbcb045ce..35a44648150 100644
--- a/drivers/media/i2c/saa7115.c
+++ b/drivers/media/i2c/saa7115.c
@@ -1582,7 +1582,6 @@ static const struct v4l2_subdev_core_ops saa711x_core_ops = {
.s_ctrl = v4l2_subdev_s_ctrl,
.queryctrl = v4l2_subdev_queryctrl,
.querymenu = v4l2_subdev_querymenu,
- .s_std = saa711x_s_std,
.reset = saa711x_reset,
.s_gpio = saa711x_s_gpio,
#ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -1601,6 +1600,7 @@ static const struct v4l2_subdev_audio_ops saa711x_audio_ops = {
};
static const struct v4l2_subdev_video_ops saa711x_video_ops = {
+ .s_std = saa711x_s_std,
.s_routing = saa711x_s_routing,
.s_crystal_freq = saa711x_s_crystal_freq,
.s_mbus_fmt = saa711x_s_mbus_fmt,
diff --git a/drivers/media/i2c/saa717x.c b/drivers/media/i2c/saa717x.c
index 401ca114ab9..6922a9f9a5c 100644
--- a/drivers/media/i2c/saa717x.c
+++ b/drivers/media/i2c/saa717x.c
@@ -1198,7 +1198,6 @@ static const struct v4l2_subdev_core_ops saa717x_core_ops = {
.g_register = saa717x_g_register,
.s_register = saa717x_s_register,
#endif
- .s_std = saa717x_s_std,
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
@@ -1216,6 +1215,7 @@ static const struct v4l2_subdev_tuner_ops saa717x_tuner_ops = {
};
static const struct v4l2_subdev_video_ops saa717x_video_ops = {
+ .s_std = saa717x_s_std,
.s_routing = saa717x_s_video_routing,
.s_mbus_fmt = saa717x_s_mbus_fmt,
.s_stream = saa717x_s_stream,
diff --git a/drivers/media/i2c/saa7191.c b/drivers/media/i2c/saa7191.c
index 606a4baf944..8e9699268a6 100644
--- a/drivers/media/i2c/saa7191.c
+++ b/drivers/media/i2c/saa7191.c
@@ -573,10 +573,10 @@ static int saa7191_g_input_status(struct v4l2_subdev *sd, u32 *status)
static const struct v4l2_subdev_core_ops saa7191_core_ops = {
.g_ctrl = saa7191_g_ctrl,
.s_ctrl = saa7191_s_ctrl,
- .s_std = saa7191_s_std,
};
static const struct v4l2_subdev_video_ops saa7191_video_ops = {
+ .s_std = saa7191_s_std,
.s_routing = saa7191_s_routing,
.querystd = saa7191_querystd,
.g_input_status = saa7191_g_input_status,
diff --git a/drivers/media/i2c/smiapp-pll.h b/drivers/media/i2c/smiapp-pll.h
index a4a649834a1..5ce2b61da3c 100644
--- a/drivers/media/i2c/smiapp-pll.h
+++ b/drivers/media/i2c/smiapp-pll.h
@@ -46,7 +46,7 @@ struct smiapp_pll {
uint8_t bus_width;
} parallel;
};
- uint8_t flags;
+ unsigned long flags;
uint8_t binning_horizontal;
uint8_t binning_vertical;
uint8_t scale_m;
diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c
index 8741cae9c9f..06fb03291d5 100644
--- a/drivers/media/i2c/smiapp/smiapp-core.c
+++ b/drivers/media/i2c/smiapp/smiapp-core.c
@@ -606,7 +606,7 @@ static int smiapp_get_limits(struct smiapp_sensor *sensor, int const *limit,
if (rval)
return rval;
sensor->limits[limit[i]] = val;
- dev_dbg(&client->dev, "0x%8.8x \"%s\" = %d, 0x%x\n",
+ dev_dbg(&client->dev, "0x%8.8x \"%s\" = %u, 0x%x\n",
smiapp_reg_limits[limit[i]].addr,
smiapp_reg_limits[limit[i]].what, val, val);
}
@@ -741,8 +741,8 @@ static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor)
if (rval)
return rval;
- dev_dbg(&client->dev, "bpp %d, compressed %d\n",
- fmt >> 8, (u8)fmt);
+ dev_dbg(&client->dev, "%u: bpp %u, compressed %u\n",
+ i, fmt >> 8, (u8)fmt);
for (j = 0; j < ARRAY_SIZE(smiapp_csi_data_formats); j++) {
const struct smiapp_csi_data_format *f =
@@ -1128,7 +1128,7 @@ static int smiapp_power_on(struct smiapp_sensor *sensor)
}
usleep_range(1000, 1000);
- if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
+ if (gpio_is_valid(sensor->platform_data->xshutdown))
gpio_set_value(sensor->platform_data->xshutdown, 1);
sleep = SMIAPP_RESET_DELAY(sensor->platform_data->ext_clk);
@@ -1238,7 +1238,7 @@ static int smiapp_power_on(struct smiapp_sensor *sensor)
return 0;
out_cci_addr_fail:
- if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
+ if (gpio_is_valid(sensor->platform_data->xshutdown))
gpio_set_value(sensor->platform_data->xshutdown, 0);
if (sensor->platform_data->set_xclk)
sensor->platform_data->set_xclk(&sensor->src->sd, 0);
@@ -1264,7 +1264,7 @@ static void smiapp_power_off(struct smiapp_sensor *sensor)
SMIAPP_REG_U8_SOFTWARE_RESET,
SMIAPP_SOFTWARE_RESET);
- if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
+ if (gpio_is_valid(sensor->platform_data->xshutdown))
gpio_set_value(sensor->platform_data->xshutdown, 0);
if (sensor->platform_data->set_xclk)
sensor->platform_data->set_xclk(&sensor->src->sd, 0);
@@ -1766,7 +1766,7 @@ static void smiapp_set_compose_binner(struct v4l2_subdev *subdev,
struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
unsigned int i;
unsigned int binh = 1, binv = 1;
- unsigned int best = scaling_goodness(
+ int best = scaling_goodness(
subdev,
crops[SMIAPP_PAD_SINK]->width, sel->r.width,
crops[SMIAPP_PAD_SINK]->height, sel->r.height, sel->flags);
@@ -2355,17 +2355,17 @@ static int smiapp_registered(struct v4l2_subdev *subdev)
unsigned int i;
int rval;
- sensor->vana = devm_regulator_get(&client->dev, "VANA");
+ sensor->vana = devm_regulator_get(&client->dev, "vana");
if (IS_ERR(sensor->vana)) {
dev_err(&client->dev, "could not get regulator for vana\n");
- return -ENODEV;
+ return PTR_ERR(sensor->vana);
}
if (!sensor->platform_data->set_xclk) {
sensor->ext_clk = devm_clk_get(&client->dev, "ext_clk");
if (IS_ERR(sensor->ext_clk)) {
dev_err(&client->dev, "could not get clock\n");
- return -ENODEV;
+ return PTR_ERR(sensor->ext_clk);
}
rval = clk_set_rate(sensor->ext_clk,
@@ -2374,18 +2374,19 @@ static int smiapp_registered(struct v4l2_subdev *subdev)
dev_err(&client->dev,
"unable to set clock freq to %u\n",
sensor->platform_data->ext_clk);
- return -ENODEV;
+ return rval;
}
}
- if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) {
- if (devm_gpio_request_one(&client->dev,
- sensor->platform_data->xshutdown, 0,
- "SMIA++ xshutdown") != 0) {
+ if (gpio_is_valid(sensor->platform_data->xshutdown)) {
+ rval = devm_gpio_request_one(
+ &client->dev, sensor->platform_data->xshutdown, 0,
+ "SMIA++ xshutdown");
+ if (rval < 0) {
dev_err(&client->dev,
"unable to acquire reset gpio %d\n",
sensor->platform_data->xshutdown);
- return -ENODEV;
+ return rval;
}
}
@@ -2423,6 +2424,12 @@ static int smiapp_registered(struct v4l2_subdev *subdev)
sensor->hvflip_inv_mask = SMIAPP_IMAGE_ORIENTATION_HFLIP |
SMIAPP_IMAGE_ORIENTATION_VFLIP;
+ rval = smiapp_call_quirk(sensor, limits);
+ if (rval) {
+ dev_err(&client->dev, "limits quirks failed\n");
+ goto out_power_off;
+ }
+
rval = smiapp_get_mbus_formats(sensor);
if (rval) {
rval = -ENODEV;
@@ -2483,12 +2490,6 @@ static int smiapp_registered(struct v4l2_subdev *subdev)
}
}
- rval = smiapp_call_quirk(sensor, limits);
- if (rval) {
- dev_err(&client->dev, "limits quirks failed\n");
- goto out_nvm_release;
- }
-
/* We consider this as profile 0 sensor if any of these are zero. */
if (!sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV] ||
!sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV] ||
@@ -2543,8 +2544,9 @@ static int smiapp_registered(struct v4l2_subdev *subdev)
}
snprintf(this->sd.name,
- sizeof(this->sd.name), "%s %s",
- sensor->minfo.name, _this->name);
+ sizeof(this->sd.name), "%s %d-%4.4x %s",
+ sensor->minfo.name, i2c_adapter_id(client->adapter),
+ client->addr, _this->name);
this->sink_fmt.width =
sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1;
@@ -2616,12 +2618,11 @@ static int smiapp_registered(struct v4l2_subdev *subdev)
pll->bus_type = SMIAPP_PLL_BUS_TYPE_CSI2;
pll->csi2.lanes = sensor->platform_data->lanes;
pll->ext_clk_freq_hz = sensor->platform_data->ext_clk;
+ pll->flags = smiapp_call_quirk(sensor, pll_flags);
+
/* Profile 0 sensors have no separate OP clock branch. */
if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0)
pll->flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS;
- if (smiapp_needs_quirk(sensor,
- SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE))
- pll->flags |= SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE;
pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN];
rval = smiapp_update_mode(sensor);
@@ -2830,7 +2831,7 @@ static int smiapp_remove(struct i2c_client *client)
unsigned int i;
if (sensor->power_count) {
- if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
+ if (gpio_is_valid(sensor->platform_data->xshutdown))
gpio_set_value(sensor->platform_data->xshutdown, 0);
if (sensor->platform_data->set_xclk)
sensor->platform_data->set_xclk(&sensor->src->sd, 0);
diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.c b/drivers/media/i2c/smiapp/smiapp-quirk.c
index bb8c506e0e3..e0bee875212 100644
--- a/drivers/media/i2c/smiapp/smiapp-quirk.c
+++ b/drivers/media/i2c/smiapp/smiapp-quirk.c
@@ -28,7 +28,7 @@
static int smiapp_write_8(struct smiapp_sensor *sensor, u16 reg, u8 val)
{
- return smiapp_write(sensor, (SMIA_REG_8BIT << 16) | reg, val);
+ return smiapp_write(sensor, SMIAPP_REG_MK_U8(reg), val);
}
static int smiapp_write_8s(struct smiapp_sensor *sensor,
@@ -61,52 +61,6 @@ void smiapp_replace_limit(struct smiapp_sensor *sensor,
sensor->limits[limit] = val;
}
-bool smiapp_quirk_reg(struct smiapp_sensor *sensor,
- u32 reg, u32 *val)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- const struct smia_reg *sreg;
-
- if (!sensor->minfo.quirk)
- return false;
-
- sreg = sensor->minfo.quirk->regs;
-
- if (!sreg)
- return false;
-
- while (sreg->type) {
- u16 type = reg >> 16;
- u16 reg16 = reg;
-
- if (sreg->type != type || sreg->reg != reg16) {
- sreg++;
- continue;
- }
-
- switch ((u8)type) {
- case SMIA_REG_8BIT:
- dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%2.2x\n",
- reg, sreg->val);
- break;
- case SMIA_REG_16BIT:
- dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%4.4x\n",
- reg, sreg->val);
- break;
- case SMIA_REG_32BIT:
- dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%8.8x\n",
- reg, sreg->val);
- break;
- }
-
- *val = sreg->val;
-
- return true;
- }
-
- return false;
-}
-
static int jt8ew9_limits(struct smiapp_sensor *sensor)
{
if (sensor->minfo.revision_number_major < 0x03)
@@ -266,12 +220,17 @@ static int jt8ev1_post_streamoff(struct smiapp_sensor *sensor)
return smiapp_write_8(sensor, 0x3328, 0x80);
}
+static unsigned long jt8ev1_pll_flags(struct smiapp_sensor *sensor)
+{
+ return SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE;
+}
+
const struct smiapp_quirk smiapp_jt8ev1_quirk = {
.limits = jt8ev1_limits,
.post_poweron = jt8ev1_post_poweron,
.pre_streamon = jt8ev1_pre_streamon,
.post_streamoff = jt8ev1_post_streamoff,
- .flags = SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE,
+ .pll_flags = jt8ev1_pll_flags,
};
static int tcm8500md_limits(struct smiapp_sensor *sensor)
diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.h b/drivers/media/i2c/smiapp/smiapp-quirk.h
index 504a6d80ced..46e9ea8bfa0 100644
--- a/drivers/media/i2c/smiapp/smiapp-quirk.h
+++ b/drivers/media/i2c/smiapp/smiapp-quirk.h
@@ -35,19 +35,30 @@ struct smiapp_sensor;
* @post_poweron: Called always after the sensor has been fully powered on.
* @pre_streamon: Called just before streaming is enabled.
* @post_streamon: Called right after stopping streaming.
+ * @reg_access: Register access quirk. The quirk may divert the access
+ * to another register, or no register at all.
+ *
+ * @write: Is this read (false) or write (true) access?
+ * @reg: Pointer to the register to access
+ * @value: Register value, set by the caller on write, or
+ * by the quirk on read
+ *
+ * @return: 0 on success, -ENOIOCTLCMD if no register
+ * access may be done by the caller (default read
+ * value is zero), else negative error code on error
*/
struct smiapp_quirk {
int (*limits)(struct smiapp_sensor *sensor);
int (*post_poweron)(struct smiapp_sensor *sensor);
int (*pre_streamon)(struct smiapp_sensor *sensor);
int (*post_streamoff)(struct smiapp_sensor *sensor);
- const struct smia_reg *regs;
+ unsigned long (*pll_flags)(struct smiapp_sensor *sensor);
+ int (*reg_access)(struct smiapp_sensor *sensor, bool write, u32 *reg,
+ u32 *val);
unsigned long flags;
};
-/* op pix clock is for all lanes in total normally */
-#define SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0)
-#define SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY (1 << 1)
+#define SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY (1 << 0)
struct smiapp_reg_8 {
u16 reg;
@@ -56,12 +67,9 @@ struct smiapp_reg_8 {
void smiapp_replace_limit(struct smiapp_sensor *sensor,
u32 limit, u32 val);
-bool smiapp_quirk_reg(struct smiapp_sensor *sensor,
- u32 reg, u32 *val);
-#define SMIAPP_MK_QUIRK_REG(_reg, _val) \
+#define SMIAPP_MK_QUIRK_REG_8(_reg, _val) \
{ \
- .type = (_reg >> 16), \
.reg = (u16)_reg, \
.val = _val, \
}
diff --git a/drivers/media/i2c/smiapp/smiapp-reg-defs.h b/drivers/media/i2c/smiapp/smiapp-reg-defs.h
index 3aa0ca948d8..c488ef02807 100644
--- a/drivers/media/i2c/smiapp/smiapp-reg-defs.h
+++ b/drivers/media/i2c/smiapp/smiapp-reg-defs.h
@@ -21,11 +21,11 @@
* 02110-1301 USA
*
*/
-#define SMIAPP_REG_MK_U8(r) ((SMIA_REG_8BIT << 16) | (r))
-#define SMIAPP_REG_MK_U16(r) ((SMIA_REG_16BIT << 16) | (r))
-#define SMIAPP_REG_MK_U32(r) ((SMIA_REG_32BIT << 16) | (r))
+#define SMIAPP_REG_MK_U8(r) ((SMIAPP_REG_8BIT << 16) | (r))
+#define SMIAPP_REG_MK_U16(r) ((SMIAPP_REG_16BIT << 16) | (r))
+#define SMIAPP_REG_MK_U32(r) ((SMIAPP_REG_32BIT << 16) | (r))
-#define SMIAPP_REG_MK_F32(r) (SMIA_REG_FLAG_FLOAT | (SMIA_REG_32BIT << 16) | (r))
+#define SMIAPP_REG_MK_F32(r) (SMIAPP_REG_FLAG_FLOAT | (SMIAPP_REG_32BIT << 16) | (r))
#define SMIAPP_REG_U16_MODEL_ID SMIAPP_REG_MK_U16(0x0000)
#define SMIAPP_REG_U8_REVISION_NUMBER_MAJOR SMIAPP_REG_MK_U8(0x0002)
diff --git a/drivers/media/i2c/smiapp/smiapp-regs.c b/drivers/media/i2c/smiapp/smiapp-regs.c
index 4fac32cfcb3..a2098007fb7 100644
--- a/drivers/media/i2c/smiapp/smiapp-regs.c
+++ b/drivers/media/i2c/smiapp/smiapp-regs.c
@@ -114,14 +114,14 @@ static int ____smiapp_read(struct smiapp_sensor *sensor, u16 reg,
*val = 0;
/* high byte comes first */
switch (len) {
- case SMIA_REG_32BIT:
+ case SMIAPP_REG_32BIT:
*val = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) +
data[3];
break;
- case SMIA_REG_16BIT:
+ case SMIAPP_REG_16BIT:
*val = (data[0] << 8) + data[1];
break;
- case SMIA_REG_8BIT:
+ case SMIAPP_REG_8BIT:
*val = data[0];
break;
default:
@@ -165,31 +165,28 @@ static int __smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val,
bool only8)
{
struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- unsigned int len = (u8)(reg >> 16);
+ u8 len = SMIAPP_REG_WIDTH(reg);
int rval;
- if (len != SMIA_REG_8BIT && len != SMIA_REG_16BIT
- && len != SMIA_REG_32BIT)
+ if (len != SMIAPP_REG_8BIT && len != SMIAPP_REG_16BIT
+ && len != SMIAPP_REG_32BIT)
return -EINVAL;
- if (smiapp_quirk_reg(sensor, reg, val))
- goto found_quirk;
-
- if (len == SMIA_REG_8BIT && !only8)
- rval = ____smiapp_read(sensor, (u16)reg, len, val);
+ if (len == SMIAPP_REG_8BIT || !only8)
+ rval = ____smiapp_read(sensor, SMIAPP_REG_ADDR(reg), len, val);
else
- rval = ____smiapp_read_8only(sensor, (u16)reg, len, val);
+ rval = ____smiapp_read_8only(sensor, SMIAPP_REG_ADDR(reg), len,
+ val);
if (rval < 0)
return rval;
-found_quirk:
- if (reg & SMIA_REG_FLAG_FLOAT)
+ if (reg & SMIAPP_REG_FLAG_FLOAT)
*val = float_to_u32_mul_1000000(client, *val);
return 0;
}
-int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val)
+int smiapp_read_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 *val)
{
return __smiapp_read(
sensor, reg, val,
@@ -197,28 +194,47 @@ int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val)
SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY));
}
+int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val)
+{
+ int rval;
+
+ *val = 0;
+ rval = smiapp_call_quirk(sensor, reg_access, false, &reg, val);
+ if (rval == -ENOIOCTLCMD)
+ return 0;
+ if (rval < 0)
+ return rval;
+
+ return smiapp_read_no_quirk(sensor, reg, val);
+}
+
int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val)
{
+ int rval;
+
+ *val = 0;
+ rval = smiapp_call_quirk(sensor, reg_access, false, &reg, val);
+ if (rval == -ENOIOCTLCMD)
+ return 0;
+ if (rval < 0)
+ return rval;
+
return __smiapp_read(sensor, reg, val, true);
}
-/*
- * Write to a 8/16-bit register.
- * Returns zero if successful, or non-zero otherwise.
- */
-int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val)
+int smiapp_write_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 val)
{
struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
struct i2c_msg msg;
unsigned char data[6];
unsigned int retries;
- unsigned int flags = reg >> 24;
- unsigned int len = (u8)(reg >> 16);
- u16 offset = reg;
+ u8 flags = SMIAPP_REG_FLAGS(reg);
+ u8 len = SMIAPP_REG_WIDTH(reg);
+ u16 offset = SMIAPP_REG_ADDR(reg);
int r;
- if ((len != SMIA_REG_8BIT && len != SMIA_REG_16BIT &&
- len != SMIA_REG_32BIT) || flags)
+ if ((len != SMIAPP_REG_8BIT && len != SMIAPP_REG_16BIT &&
+ len != SMIAPP_REG_32BIT) || flags)
return -EINVAL;
msg.addr = client->addr;
@@ -231,14 +247,14 @@ int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val)
data[1] = (u8) (reg & 0xff);
switch (len) {
- case SMIA_REG_8BIT:
+ case SMIAPP_REG_8BIT:
data[2] = val;
break;
- case SMIA_REG_16BIT:
+ case SMIAPP_REG_16BIT:
data[2] = val >> 8;
data[3] = val;
break;
- case SMIA_REG_32BIT:
+ case SMIAPP_REG_32BIT:
data[2] = val >> 24;
data[3] = val >> 16;
data[4] = val >> 8;
@@ -271,3 +287,20 @@ int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val)
return r;
}
+
+/*
+ * Write to a 8/16-bit register.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val)
+{
+ int rval;
+
+ rval = smiapp_call_quirk(sensor, reg_access, true, &reg, &val);
+ if (rval == -ENOIOCTLCMD)
+ return 0;
+ if (rval < 0)
+ return rval;
+
+ return smiapp_write_no_quirk(sensor, reg, val);
+}
diff --git a/drivers/media/i2c/smiapp/smiapp-regs.h b/drivers/media/i2c/smiapp/smiapp-regs.h
index eefc6c84d5f..35521125a2c 100644
--- a/drivers/media/i2c/smiapp/smiapp-regs.h
+++ b/drivers/media/i2c/smiapp/smiapp-regs.h
@@ -28,22 +28,23 @@
#include <linux/i2c.h>
#include <linux/types.h>
+#define SMIAPP_REG_ADDR(reg) ((u16)reg)
+#define SMIAPP_REG_WIDTH(reg) ((u8)(reg >> 16))
+#define SMIAPP_REG_FLAGS(reg) ((u8)(reg >> 24))
+
/* Use upper 8 bits of the type field for flags */
-#define SMIA_REG_FLAG_FLOAT (1 << 24)
+#define SMIAPP_REG_FLAG_FLOAT (1 << 24)
-#define SMIA_REG_8BIT 1
-#define SMIA_REG_16BIT 2
-#define SMIA_REG_32BIT 4
-struct smia_reg {
- u16 type;
- u16 reg; /* 16-bit offset */
- u32 val; /* 8/16/32-bit value */
-};
+#define SMIAPP_REG_8BIT 1
+#define SMIAPP_REG_16BIT 2
+#define SMIAPP_REG_32BIT 4
struct smiapp_sensor;
+int smiapp_read_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 *val);
int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val);
int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val);
+int smiapp_write_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 val);
int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val);
#endif
diff --git a/drivers/media/i2c/soc_camera/tw9910.c b/drivers/media/i2c/soc_camera/tw9910.c
index ab54628d941..416402eb4f8 100644
--- a/drivers/media/i2c/soc_camera/tw9910.c
+++ b/drivers/media/i2c/soc_camera/tw9910.c
@@ -814,8 +814,6 @@ done:
}
static struct v4l2_subdev_core_ops tw9910_subdev_core_ops = {
- .s_std = tw9910_s_std,
- .g_std = tw9910_g_std,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = tw9910_g_register,
.s_register = tw9910_s_register,
@@ -872,7 +870,15 @@ static int tw9910_s_mbus_config(struct v4l2_subdev *sd,
return i2c_smbus_write_byte_data(client, OUTCTR1, val);
}
+static int tw9910_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm)
+{
+ *norm = V4L2_STD_NTSC | V4L2_STD_PAL;
+ return 0;
+}
+
static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = {
+ .s_std = tw9910_s_std,
+ .g_std = tw9910_g_std,
.s_stream = tw9910_s_stream,
.g_mbus_fmt = tw9910_g_fmt,
.s_mbus_fmt = tw9910_s_fmt,
@@ -882,6 +888,7 @@ static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = {
.enum_mbus_fmt = tw9910_enum_fmt,
.g_mbus_config = tw9910_g_mbus_config,
.s_mbus_config = tw9910_s_mbus_config,
+ .g_tvnorms = tw9910_g_tvnorms,
};
static struct v4l2_subdev_ops tw9910_subdev_ops = {
diff --git a/drivers/media/i2c/sony-btf-mpx.c b/drivers/media/i2c/sony-btf-mpx.c
index 32d82320b48..1da8004f5a8 100644
--- a/drivers/media/i2c/sony-btf-mpx.c
+++ b/drivers/media/i2c/sony-btf-mpx.c
@@ -327,18 +327,18 @@ static int sony_btf_mpx_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner
/* --------------------------------------------------------------------------*/
-static const struct v4l2_subdev_core_ops sony_btf_mpx_core_ops = {
- .s_std = sony_btf_mpx_s_std,
-};
-
static const struct v4l2_subdev_tuner_ops sony_btf_mpx_tuner_ops = {
.s_tuner = sony_btf_mpx_s_tuner,
.g_tuner = sony_btf_mpx_g_tuner,
};
+static const struct v4l2_subdev_video_ops sony_btf_mpx_video_ops = {
+ .s_std = sony_btf_mpx_s_std,
+};
+
static const struct v4l2_subdev_ops sony_btf_mpx_ops = {
- .core = &sony_btf_mpx_core_ops,
.tuner = &sony_btf_mpx_tuner_ops,
+ .video = &sony_btf_mpx_video_ops,
};
/* --------------------------------------------------------------------------*/
diff --git a/drivers/media/i2c/sr030pc30.c b/drivers/media/i2c/sr030pc30.c
index ae9432637fc..118f8ee8846 100644
--- a/drivers/media/i2c/sr030pc30.c
+++ b/drivers/media/i2c/sr030pc30.c
@@ -8,7 +8,7 @@
* and HeungJun Kim <riverful.kim@samsung.com>.
*
* Based on mt9v011 Micron Digital Image Sensor driver
- * Copyright (c) 2009 Mauro Carvalho Chehab (mchehab@redhat.com)
+ * Copyright (c) 2009 Mauro Carvalho Chehab
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c
index 04139eec8c4..656d889c1c7 100644
--- a/drivers/media/i2c/ths8200.c
+++ b/drivers/media/i2c/ths8200.c
@@ -217,8 +217,8 @@ static void ths8200_core_init(struct v4l2_subdev *sd)
/* Disable embedded syncs on the output by setting
* the amplitude to zero for all channels.
*/
- ths8200_write(sd, THS8200_DTG1_Y_SYNC_MSB, 0x2a);
- ths8200_write(sd, THS8200_DTG1_CBCR_SYNC_MSB, 0x2a);
+ ths8200_write(sd, THS8200_DTG1_Y_SYNC_MSB, 0x00);
+ ths8200_write(sd, THS8200_DTG1_CBCR_SYNC_MSB, 0x00);
}
static void ths8200_setup(struct v4l2_subdev *sd, struct v4l2_bt_timings *bt)
@@ -318,15 +318,15 @@ static void ths8200_setup(struct v4l2_subdev *sd, struct v4l2_bt_timings *bt)
(htotal(bt) >> 8) & 0x1f);
ths8200_write(sd, THS8200_DTG2_HLENGTH_HDLY_LSB, htotal(bt));
- /* v sync width transmitted */
- ths8200_write(sd, THS8200_DTG2_VLENGTH1_LSB, (bt->vsync) & 0xff);
+ /* v sync width transmitted (must add 1 to get correct output) */
+ ths8200_write(sd, THS8200_DTG2_VLENGTH1_LSB, (bt->vsync + 1) & 0xff);
ths8200_write_and_or(sd, THS8200_DTG2_VLENGTH1_MSB_VDLY1_MSB, 0x3f,
- ((bt->vsync) >> 2) & 0xc0);
+ ((bt->vsync + 1) >> 2) & 0xc0);
- /* The pixel value v sync is asserted on */
+ /* The pixel value v sync is asserted on (must add 1 to get correct output) */
ths8200_write_and_or(sd, THS8200_DTG2_VLENGTH1_MSB_VDLY1_MSB, 0xf8,
- (vtotal(bt)>>8) & 0x7);
- ths8200_write(sd, THS8200_DTG2_VDLY1_LSB, vtotal(bt));
+ ((vtotal(bt) + 1) >> 8) & 0x7);
+ ths8200_write(sd, THS8200_DTG2_VDLY1_LSB, vtotal(bt) + 1);
/* For progressive video vlength2 must be set to all 0 and vdly2 must
* be set to all 1.
@@ -336,11 +336,11 @@ static void ths8200_setup(struct v4l2_subdev *sd, struct v4l2_bt_timings *bt)
ths8200_write(sd, THS8200_DTG2_VDLY2_LSB, 0xff);
/* Internal delay factors to synchronize the sync pulses and the data */
- /* Experimental values delays (hor 4, ver 1) */
- ths8200_write(sd, THS8200_DTG2_HS_IN_DLY_MSB, (htotal(bt)>>8) & 0x1f);
- ths8200_write(sd, THS8200_DTG2_HS_IN_DLY_LSB, (htotal(bt) - 4) & 0xff);
+ /* Experimental values delays (hor 0, ver 0) */
+ ths8200_write(sd, THS8200_DTG2_HS_IN_DLY_MSB, 0);
+ ths8200_write(sd, THS8200_DTG2_HS_IN_DLY_LSB, 0);
ths8200_write(sd, THS8200_DTG2_VS_IN_DLY_MSB, 0);
- ths8200_write(sd, THS8200_DTG2_VS_IN_DLY_LSB, 1);
+ ths8200_write(sd, THS8200_DTG2_VS_IN_DLY_LSB, 0);
/* Polarity of received and transmitted sync signals */
if (bt->polarities & V4L2_DV_HSYNC_POS_POL) {
@@ -356,7 +356,7 @@ static void ths8200_setup(struct v4l2_subdev *sd, struct v4l2_bt_timings *bt)
/* Timing of video input bus is derived from HS, VS, and FID dedicated
* inputs
*/
- ths8200_write(sd, THS8200_DTG2_CNTL, 0x47 | polarity);
+ ths8200_write(sd, THS8200_DTG2_CNTL, 0x44 | polarity);
/* leave reset */
ths8200_s_stream(sd, true);
@@ -410,6 +410,9 @@ static int ths8200_g_dv_timings(struct v4l2_subdev *sd,
static int ths8200_enum_dv_timings(struct v4l2_subdev *sd,
struct v4l2_enum_dv_timings *timings)
{
+ if (timings->pad != 0)
+ return -EINVAL;
+
return v4l2_enum_dv_timings_cap(timings, &ths8200_timings_cap,
NULL, NULL);
}
@@ -417,6 +420,9 @@ static int ths8200_enum_dv_timings(struct v4l2_subdev *sd,
static int ths8200_dv_timings_cap(struct v4l2_subdev *sd,
struct v4l2_dv_timings_cap *cap)
{
+ if (cap->pad != 0)
+ return -EINVAL;
+
*cap = ths8200_timings_cap;
return 0;
}
@@ -426,6 +432,9 @@ static const struct v4l2_subdev_video_ops ths8200_video_ops = {
.s_stream = ths8200_s_stream,
.s_dv_timings = ths8200_s_dv_timings,
.g_dv_timings = ths8200_g_dv_timings,
+};
+
+static const struct v4l2_subdev_pad_ops ths8200_pad_ops = {
.enum_dv_timings = ths8200_enum_dv_timings,
.dv_timings_cap = ths8200_dv_timings_cap,
};
@@ -434,6 +443,7 @@ static const struct v4l2_subdev_video_ops ths8200_video_ops = {
static const struct v4l2_subdev_ops ths8200_ops = {
.core = &ths8200_core_ops,
.video = &ths8200_video_ops,
+ .pad = &ths8200_pad_ops,
};
static int ths8200_probe(struct i2c_client *client,
diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c
index d76c53a8f02..070c152da95 100644
--- a/drivers/media/i2c/tvaudio.c
+++ b/drivers/media/i2c/tvaudio.c
@@ -1862,7 +1862,6 @@ static const struct v4l2_subdev_core_ops tvaudio_core_ops = {
.s_ctrl = v4l2_subdev_s_ctrl,
.queryctrl = v4l2_subdev_queryctrl,
.querymenu = v4l2_subdev_querymenu,
- .s_std = tvaudio_s_std,
};
static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = {
@@ -1876,10 +1875,15 @@ static const struct v4l2_subdev_audio_ops tvaudio_audio_ops = {
.s_routing = tvaudio_s_routing,
};
+static const struct v4l2_subdev_video_ops tvaudio_video_ops = {
+ .s_std = tvaudio_s_std,
+};
+
static const struct v4l2_subdev_ops tvaudio_ops = {
.core = &tvaudio_core_ops,
.tuner = &tvaudio_tuner_ops,
.audio = &tvaudio_audio_ops,
+ .video = &tvaudio_video_ops,
};
/* ----------------------------------------------------------------------- */
diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c
index 83d85df4853..b9dabc9f405 100644
--- a/drivers/media/i2c/tvp514x.c
+++ b/drivers/media/i2c/tvp514x.c
@@ -36,6 +36,7 @@
#include <linux/module.h>
#include <linux/v4l2-mediabus.h>
#include <linux/of.h>
+#include <linux/of_graph.h>
#include <media/v4l2-async.h>
#include <media/v4l2-device.h>
@@ -1009,10 +1010,10 @@ static const struct v4l2_subdev_core_ops tvp514x_core_ops = {
.s_ctrl = v4l2_subdev_s_ctrl,
.queryctrl = v4l2_subdev_queryctrl,
.querymenu = v4l2_subdev_querymenu,
- .s_std = tvp514x_s_std,
};
static const struct v4l2_subdev_video_ops tvp514x_video_ops = {
+ .s_std = tvp514x_s_std,
.s_routing = tvp514x_s_routing,
.querystd = tvp514x_querystd,
.enum_mbus_fmt = tvp514x_enum_mbus_fmt,
@@ -1068,7 +1069,7 @@ tvp514x_get_pdata(struct i2c_client *client)
if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node)
return client->dev.platform_data;
- endpoint = v4l2_of_get_next_endpoint(client->dev.of_node, NULL);
+ endpoint = of_graph_get_next_endpoint(client->dev.of_node, NULL);
if (!endpoint)
return NULL;
diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
index 542d2528b3f..a9121254e37 100644
--- a/drivers/media/i2c/tvp5150.c
+++ b/drivers/media/i2c/tvp5150.c
@@ -16,9 +16,9 @@
#include "tvp5150_reg.h"
-#define TVP5150_H_MAX 720
-#define TVP5150_V_MAX_525_60 480
-#define TVP5150_V_MAX_OTHERS 576
+#define TVP5150_H_MAX 720U
+#define TVP5150_V_MAX_525_60 480U
+#define TVP5150_V_MAX_OTHERS 576U
#define TVP5150_MAX_CROP_LEFT 511
#define TVP5150_MAX_CROP_TOP 127
#define TVP5150_CROP_SHIFT 2
@@ -29,7 +29,7 @@ MODULE_LICENSE("GPL");
static int debug;
-module_param(debug, int, 0);
+module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug level (0-2)");
struct tvp5150 {
@@ -913,7 +913,7 @@ static int tvp5150_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)
static int tvp5150_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
- struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd);
+ struct tvp5150 *decoder = to_tvp5150(sd);
a->c = decoder->rect;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
@@ -923,7 +923,7 @@ static int tvp5150_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
static int tvp5150_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
{
- struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd);
+ struct tvp5150 *decoder = to_tvp5150(sd);
v4l2_std_id std;
if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
@@ -1063,7 +1063,6 @@ static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = {
static const struct v4l2_subdev_core_ops tvp5150_core_ops = {
.log_status = tvp5150_log_status,
- .s_std = tvp5150_s_std,
.reset = tvp5150_reset,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = tvp5150_g_register,
@@ -1076,6 +1075,7 @@ static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = {
};
static const struct v4l2_subdev_video_ops tvp5150_video_ops = {
+ .s_std = tvp5150_s_std,
.s_routing = tvp5150_s_routing,
.enum_mbus_fmt = tvp5150_enum_mbus_fmt,
.s_mbus_fmt = tvp5150_mbus_fmt,
diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c
index 912e1cccdd1..11f2387e1da 100644
--- a/drivers/media/i2c/tvp7002.c
+++ b/drivers/media/i2c/tvp7002.c
@@ -30,6 +30,7 @@
#include <linux/videodev2.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_graph.h>
#include <linux/v4l2-dv-timings.h>
#include <media/tvp7002.h>
#include <media/v4l2-async.h>
@@ -832,6 +833,9 @@ static int tvp7002_log_status(struct v4l2_subdev *sd)
static int tvp7002_enum_dv_timings(struct v4l2_subdev *sd,
struct v4l2_enum_dv_timings *timings)
{
+ if (timings->pad != 0)
+ return -EINVAL;
+
/* Check requested format index is within range */
if (timings->index >= NUM_TIMINGS)
return -EINVAL;
@@ -923,7 +927,6 @@ static const struct v4l2_subdev_core_ops tvp7002_core_ops = {
static const struct v4l2_subdev_video_ops tvp7002_video_ops = {
.g_dv_timings = tvp7002_g_dv_timings,
.s_dv_timings = tvp7002_s_dv_timings,
- .enum_dv_timings = tvp7002_enum_dv_timings,
.query_dv_timings = tvp7002_query_dv_timings,
.s_stream = tvp7002_s_stream,
.g_mbus_fmt = tvp7002_mbus_fmt,
@@ -937,6 +940,7 @@ static const struct v4l2_subdev_pad_ops tvp7002_pad_ops = {
.enum_mbus_code = tvp7002_enum_mbus_code,
.get_fmt = tvp7002_get_pad_format,
.set_fmt = tvp7002_set_pad_format,
+ .enum_dv_timings = tvp7002_enum_dv_timings,
};
/* V4L2 top level operation handlers */
@@ -957,7 +961,7 @@ tvp7002_get_pdata(struct i2c_client *client)
if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node)
return client->dev.platform_data;
- endpoint = v4l2_of_get_next_endpoint(client->dev.of_node, NULL);
+ endpoint = of_graph_get_next_endpoint(client->dev.of_node, NULL);
if (!endpoint)
return NULL;
diff --git a/drivers/media/i2c/tw2804.c b/drivers/media/i2c/tw2804.c
index f58607df619..7347480c0b0 100644
--- a/drivers/media/i2c/tw2804.c
+++ b/drivers/media/i2c/tw2804.c
@@ -342,12 +342,12 @@ static const struct v4l2_ctrl_ops tw2804_ctrl_ops = {
};
static const struct v4l2_subdev_video_ops tw2804_video_ops = {
+ .s_std = tw2804_s_std,
.s_routing = tw2804_s_video_routing,
};
static const struct v4l2_subdev_core_ops tw2804_core_ops = {
.log_status = tw2804_log_status,
- .s_std = tw2804_s_std,
};
static const struct v4l2_subdev_ops tw2804_ops = {
diff --git a/drivers/media/i2c/tw9903.c b/drivers/media/i2c/tw9903.c
index 285b759a5f7..12c7d211a4a 100644
--- a/drivers/media/i2c/tw9903.c
+++ b/drivers/media/i2c/tw9903.c
@@ -187,10 +187,10 @@ static const struct v4l2_ctrl_ops tw9903_ctrl_ops = {
static const struct v4l2_subdev_core_ops tw9903_core_ops = {
.log_status = tw9903_log_status,
- .s_std = tw9903_s_std,
};
static const struct v4l2_subdev_video_ops tw9903_video_ops = {
+ .s_std = tw9903_s_std,
.s_routing = tw9903_s_video_routing,
};
diff --git a/drivers/media/i2c/tw9906.c b/drivers/media/i2c/tw9906.c
index f6bef25bd9c..2672d89265f 100644
--- a/drivers/media/i2c/tw9906.c
+++ b/drivers/media/i2c/tw9906.c
@@ -157,10 +157,10 @@ static const struct v4l2_ctrl_ops tw9906_ctrl_ops = {
static const struct v4l2_subdev_core_ops tw9906_core_ops = {
.log_status = tw9906_log_status,
- .s_std = tw9906_s_std,
};
static const struct v4l2_subdev_video_ops tw9906_video_ops = {
+ .s_std = tw9906_s_std,
.s_routing = tw9906_s_video_routing,
};
diff --git a/drivers/media/i2c/vp27smpx.c b/drivers/media/i2c/vp27smpx.c
index 6a3a3ff7ee6..819ab6d1298 100644
--- a/drivers/media/i2c/vp27smpx.c
+++ b/drivers/media/i2c/vp27smpx.c
@@ -124,7 +124,6 @@ static int vp27smpx_log_status(struct v4l2_subdev *sd)
static const struct v4l2_subdev_core_ops vp27smpx_core_ops = {
.log_status = vp27smpx_log_status,
- .s_std = vp27smpx_s_std,
};
static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = {
@@ -133,9 +132,14 @@ static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = {
.g_tuner = vp27smpx_g_tuner,
};
+static const struct v4l2_subdev_video_ops vp27smpx_video_ops = {
+ .s_std = vp27smpx_s_std,
+};
+
static const struct v4l2_subdev_ops vp27smpx_ops = {
.core = &vp27smpx_core_ops,
.tuner = &vp27smpx_tuner_ops,
+ .video = &vp27smpx_video_ops,
};
/* ----------------------------------------------------------------------- */
diff --git a/drivers/media/i2c/vpx3220.c b/drivers/media/i2c/vpx3220.c
index ece90df6a04..016e766e72b 100644
--- a/drivers/media/i2c/vpx3220.c
+++ b/drivers/media/i2c/vpx3220.c
@@ -457,10 +457,10 @@ static const struct v4l2_subdev_core_ops vpx3220_core_ops = {
.s_ctrl = v4l2_subdev_s_ctrl,
.queryctrl = v4l2_subdev_queryctrl,
.querymenu = v4l2_subdev_querymenu,
- .s_std = vpx3220_s_std,
};
static const struct v4l2_subdev_video_ops vpx3220_video_ops = {
+ .s_std = vpx3220_s_std,
.s_routing = vpx3220_s_routing,
.s_stream = vpx3220_s_stream,
.querystd = vpx3220_querystd,