diff options
Diffstat (limited to 'drivers/media/i2c')
37 files changed, 1344 insertions, 687 deletions
diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index 1b7ecfd88673..fada17566205 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_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 5e638b159452..ac1cdbe251a3 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -461,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, @@ -472,7 +473,6 @@ 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, }; diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c index d45e0e3a781d..df461b07b2f7 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/adv7511.c b/drivers/media/i2c/adv7511.c index 942ca4b99297..f98acf4aafd4 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_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 98cc5407f1b1..1778d320272e 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); @@ -1663,7 +1997,7 @@ 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_edid *edid) 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; @@ -1731,20 +2063,20 @@ static int get_edid_spa_location(const u8 *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_edid *edid) 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_edid *edid) /* 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 636ac08925f6..0d554919cdd5 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -1399,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); } @@ -1406,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; } @@ -2000,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; } @@ -2029,8 +2036,6 @@ static int adv7842_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) return -EINVAL; if (edid->start_block == 1) edid->blocks = 1; - if (!edid->edid) - return -EINVAL; switch (edid->pad) { case ADV7842_EDID_PORT_A: @@ -2065,8 +2070,6 @@ static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_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], @@ -2610,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 */ @@ -2690,9 +2699,6 @@ 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); - if (pdata->hpa_auto) { /* HPA auto, HPA 0.5s after Edid set and Cable detect */ hdmi_write(sd, 0x69, 0x5c); @@ -2869,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 @@ -2880,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, @@ -2897,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 369cf6ff88f7..76b334a6a56d 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 2e3771d57354..e453a3ffe7d1 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/ks0127.c b/drivers/media/i2c/ks0127.c index c3e94ae82c03..25b81bc58c81 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/m5mols/m5mols_capture.c b/drivers/media/i2c/m5mols/m5mols_capture.c index ab34ccedf31e..1a03d02bd4d1 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 a9110d8bbbcd..2cace7313a22 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 8190fec68080..4d9c6bc34265 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 33daace81297..e18797ff7faf 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -647,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 = @@ -655,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, @@ -709,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); } @@ -735,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) diff --git a/drivers/media/i2c/saa6752hs.c b/drivers/media/i2c/saa6752hs.c index 8272c0b9c5bf..04e9e55018a5 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 ac43e929a1d6..99689ee57d7e 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 afdbcb045cee..35a44648150e 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 401ca114ab99..6922a9f9a5cd 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 606a4baf944d..8e9699268a63 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 a4a649834a18..5ce2b61da3c5 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 8741cae9c9f2..06fb03291d59 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 bb8c506e0e3d..e0bee8752122 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 504a6d80ced5..46e9ea8bfa08 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 3aa0ca948d87..c488ef028074 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 4fac32cfcb3f..a2098007fb70 100644 --- a/drivers/media/i2c/smiapp/smiapp-regs.c +++ b/drivers/media/i2c/smiapp/smiapp-regs.c @@ -114,14 +114,14 @@ static int ____smiapp_read(struct smiapp_sensor *sensor, u16 reg, *val = 0; /* high byte comes first */ switch (len) { - case SMIA_REG_32BIT: + case SMIAPP_REG_32BIT: *val = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3]; break; - case SMIA_REG_16BIT: + case SMIAPP_REG_16BIT: *val = (data[0] << 8) + data[1]; break; - case SMIA_REG_8BIT: + case SMIAPP_REG_8BIT: *val = data[0]; break; default: @@ -165,31 +165,28 @@ static int __smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val, bool only8) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned int len = (u8)(reg >> 16); + u8 len = SMIAPP_REG_WIDTH(reg); int rval; - if (len != SMIA_REG_8BIT && len != SMIA_REG_16BIT - && len != SMIA_REG_32BIT) + if (len != SMIAPP_REG_8BIT && len != SMIAPP_REG_16BIT + && len != SMIAPP_REG_32BIT) return -EINVAL; - if (smiapp_quirk_reg(sensor, reg, val)) - goto found_quirk; - - if (len == SMIA_REG_8BIT && !only8) - rval = ____smiapp_read(sensor, (u16)reg, len, val); + if (len == SMIAPP_REG_8BIT || !only8) + rval = ____smiapp_read(sensor, SMIAPP_REG_ADDR(reg), len, val); else - rval = ____smiapp_read_8only(sensor, (u16)reg, len, val); + rval = ____smiapp_read_8only(sensor, SMIAPP_REG_ADDR(reg), len, + val); if (rval < 0) return rval; -found_quirk: - if (reg & SMIA_REG_FLAG_FLOAT) + if (reg & SMIAPP_REG_FLAG_FLOAT) *val = float_to_u32_mul_1000000(client, *val); return 0; } -int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val) +int smiapp_read_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 *val) { return __smiapp_read( sensor, reg, val, @@ -197,28 +194,47 @@ int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val) SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY)); } +int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val) +{ + int rval; + + *val = 0; + rval = smiapp_call_quirk(sensor, reg_access, false, ®, val); + if (rval == -ENOIOCTLCMD) + return 0; + if (rval < 0) + return rval; + + return smiapp_read_no_quirk(sensor, reg, val); +} + int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val) { + int rval; + + *val = 0; + rval = smiapp_call_quirk(sensor, reg_access, false, ®, val); + if (rval == -ENOIOCTLCMD) + return 0; + if (rval < 0) + return rval; + return __smiapp_read(sensor, reg, val, true); } -/* - * Write to a 8/16-bit register. - * Returns zero if successful, or non-zero otherwise. - */ -int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val) +int smiapp_write_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 val) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); struct i2c_msg msg; unsigned char data[6]; unsigned int retries; - unsigned int flags = reg >> 24; - unsigned int len = (u8)(reg >> 16); - u16 offset = reg; + u8 flags = SMIAPP_REG_FLAGS(reg); + u8 len = SMIAPP_REG_WIDTH(reg); + u16 offset = SMIAPP_REG_ADDR(reg); int r; - if ((len != SMIA_REG_8BIT && len != SMIA_REG_16BIT && - len != SMIA_REG_32BIT) || flags) + if ((len != SMIAPP_REG_8BIT && len != SMIAPP_REG_16BIT && + len != SMIAPP_REG_32BIT) || flags) return -EINVAL; msg.addr = client->addr; @@ -231,14 +247,14 @@ int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val) data[1] = (u8) (reg & 0xff); switch (len) { - case SMIA_REG_8BIT: + case SMIAPP_REG_8BIT: data[2] = val; break; - case SMIA_REG_16BIT: + case SMIAPP_REG_16BIT: data[2] = val >> 8; data[3] = val; break; - case SMIA_REG_32BIT: + case SMIAPP_REG_32BIT: data[2] = val >> 24; data[3] = val >> 16; data[4] = val >> 8; @@ -271,3 +287,20 @@ int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val) return r; } + +/* + * Write to a 8/16-bit register. + * Returns zero if successful, or non-zero otherwise. + */ +int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val) +{ + int rval; + + rval = smiapp_call_quirk(sensor, reg_access, true, ®, &val); + if (rval == -ENOIOCTLCMD) + return 0; + if (rval < 0) + return rval; + + return smiapp_write_no_quirk(sensor, reg, val); +} diff --git a/drivers/media/i2c/smiapp/smiapp-regs.h b/drivers/media/i2c/smiapp/smiapp-regs.h index eefc6c84d5fe..35521125a2cc 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 ab54628d9411..416402eb4f82 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 32d82320b485..1da8004f5a8e 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/ths8200.c b/drivers/media/i2c/ths8200.c index f72561e79739..656d889c1c79 100644 --- a/drivers/media/i2c/ths8200.c +++ b/drivers/media/i2c/ths8200.c @@ -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 d76c53a8f027..070c152da95a 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 ca001178c5bf..b9dabc9f4050 100644 --- a/drivers/media/i2c/tvp514x.c +++ b/drivers/media/i2c/tvp514x.c @@ -1010,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, diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 4fd3688e1164..a9121254e37a 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -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 c4e1e2cb3094..11f2387e1dab 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -833,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; @@ -924,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, @@ -938,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 */ diff --git a/drivers/media/i2c/tw2804.c b/drivers/media/i2c/tw2804.c index f58607df6193..7347480c0b0c 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 285b759a5f7f..12c7d211a4a4 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 f6bef25bd9ce..2672d89265ff 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 6a3a3ff7ee6a..819ab6d12989 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 ece90df6a043..016e766e72ba 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, |