diff options
Diffstat (limited to 'drivers/media/i2c/tvp5150.c')
| -rw-r--r-- | drivers/media/i2c/tvp5150.c | 2083 |
1 files changed, 1572 insertions, 511 deletions
diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 89c0b13463b7..e3675c744d9e 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -1,46 +1,98 @@ -/* - * tvp5150 - Texas Instruments TVP5150A/AM1 video decoder driver - * - * Copyright (c) 2005,2006 Mauro Carvalho Chehab (mchehab@infradead.org) - * This code is placed under the terms of the GNU General Public License v2 - */ +// SPDX-License-Identifier: GPL-2.0 +// +// tvp5150 - Texas Instruments TVP5150A/AM1 and TVP5151 video decoder driver +// +// Copyright (c) 2005,2006 Mauro Carvalho Chehab <mchehab@kernel.org> +#include <dt-bindings/media/tvp5150.h> #include <linux/i2c.h> #include <linux/slab.h> #include <linux/videodev2.h> #include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/interrupt.h> #include <linux/module.h> +#include <linux/of_graph.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <media/v4l2-async.h> #include <media/v4l2-device.h> -#include <media/tvp5150.h> +#include <media/v4l2-event.h> #include <media/v4l2-ctrls.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-mc.h> +#include <media/v4l2-rect.h> #include "tvp5150_reg.h" -#define TVP5150_H_MAX 720 -#define TVP5150_V_MAX_525_60 480 -#define TVP5150_V_MAX_OTHERS 576 +#define TVP5150_H_MAX 720U +#define TVP5150_V_MAX_525_60 480U +#define TVP5150_V_MAX_OTHERS 576U #define TVP5150_MAX_CROP_LEFT 511 #define TVP5150_MAX_CROP_TOP 127 #define TVP5150_CROP_SHIFT 2 - -MODULE_DESCRIPTION("Texas Instruments TVP5150A video decoder driver"); +#define TVP5150_MBUS_FMT MEDIA_BUS_FMT_UYVY8_2X8 +#define TVP5150_FIELD V4L2_FIELD_ALTERNATE +#define TVP5150_COLORSPACE V4L2_COLORSPACE_SMPTE170M +#define TVP5150_STD_MASK (V4L2_STD_NTSC | \ + V4L2_STD_NTSC_443 | \ + V4L2_STD_PAL | \ + V4L2_STD_PAL_M | \ + V4L2_STD_PAL_N | \ + V4L2_STD_PAL_Nc | \ + V4L2_STD_SECAM) + +#define TVP5150_MAX_CONNECTORS 3 /* Check dt-bindings for more information */ + +MODULE_DESCRIPTION("Texas Instruments TVP5150A/TVP5150AM1/TVP5151 video decoder driver"); MODULE_AUTHOR("Mauro Carvalho Chehab"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); static int debug; -module_param(debug, int, 0); +module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Debug level (0-2)"); +#define dprintk0(__dev, __arg...) dev_dbg_lvl(__dev, 0, 0, __arg) + +enum tvp5150_pads { + TVP5150_PAD_AIP1A, + TVP5150_PAD_AIP1B, + TVP5150_PAD_VID_OUT, + TVP5150_NUM_PADS +}; + +struct tvp5150_connector { + struct v4l2_fwnode_connector base; + struct media_entity ent; + struct media_pad pad; +}; + struct tvp5150 { struct v4l2_subdev sd; + + struct media_pad pads[TVP5150_NUM_PADS]; + struct tvp5150_connector connectors[TVP5150_MAX_CONNECTORS]; + struct tvp5150_connector *cur_connector; + unsigned int connectors_num; + struct v4l2_ctrl_handler hdl; struct v4l2_rect rect; + struct regmap *regmap; + int irq; v4l2_std_id norm; /* Current set standard */ + v4l2_std_id detected_norm; u32 input; u32 output; + u32 oe; int enable; + bool lock; + + u16 dev_id; + u16 rom_ver; + + enum v4l2_mbus_type mbus_type; }; static inline struct tvp5150 *to_tvp5150(struct v4l2_subdev *sd) @@ -55,180 +107,153 @@ static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr) { - struct i2c_client *c = v4l2_get_subdevdata(sd); - unsigned char buffer[1]; - int rc; - - buffer[0] = addr; - - rc = i2c_master_send(c, buffer, 1); - if (rc < 0) { - v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc); - return rc; - } - - msleep(10); - - rc = i2c_master_recv(c, buffer, 1); - if (rc < 0) { - v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc); - return rc; - } - - v4l2_dbg(2, debug, sd, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]); + struct tvp5150 *decoder = to_tvp5150(sd); + int ret, val; - return (buffer[0]); -} + ret = regmap_read(decoder->regmap, addr, &val); + if (ret < 0) + return ret; -static inline void tvp5150_write(struct v4l2_subdev *sd, unsigned char addr, - unsigned char value) -{ - struct i2c_client *c = v4l2_get_subdevdata(sd); - unsigned char buffer[2]; - int rc; - - buffer[0] = addr; - buffer[1] = value; - v4l2_dbg(2, debug, sd, "tvp5150: writing 0x%02x 0x%02x\n", buffer[0], buffer[1]); - if (2 != (rc = i2c_master_send(c, buffer, 2))) - v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 2)\n", rc); + return val; } static void dump_reg_range(struct v4l2_subdev *sd, char *s, u8 init, const u8 end, int max_line) { - int i = 0; + u8 buf[16]; + int i = 0, j, len; - while (init != (u8)(end + 1)) { - if ((i % max_line) == 0) { - if (i > 0) - printk("\n"); - printk("tvp5150: %s reg 0x%02x = ", s, init); - } - printk("%02x ", tvp5150_read(sd, init)); + if (max_line > 16) { + dprintk0(sd->dev, "too much data to dump\n"); + return; + } + + for (i = init; i < end; i += max_line) { + len = (end - i > max_line) ? max_line : end - i; + + for (j = 0; j < len; j++) + buf[j] = tvp5150_read(sd, i + j); - init++; - i++; + dprintk0(sd->dev, "%s reg %02x = %*ph\n", s, i, len, buf); } - printk("\n"); } static int tvp5150_log_status(struct v4l2_subdev *sd) { - printk("tvp5150: Video input source selection #1 = 0x%02x\n", - tvp5150_read(sd, TVP5150_VD_IN_SRC_SEL_1)); - printk("tvp5150: Analog channel controls = 0x%02x\n", - tvp5150_read(sd, TVP5150_ANAL_CHL_CTL)); - printk("tvp5150: Operation mode controls = 0x%02x\n", - tvp5150_read(sd, TVP5150_OP_MODE_CTL)); - printk("tvp5150: Miscellaneous controls = 0x%02x\n", - tvp5150_read(sd, TVP5150_MISC_CTL)); - printk("tvp5150: Autoswitch mask= 0x%02x\n", - tvp5150_read(sd, TVP5150_AUTOSW_MSK)); - printk("tvp5150: Color killer threshold control = 0x%02x\n", - tvp5150_read(sd, TVP5150_COLOR_KIL_THSH_CTL)); - printk("tvp5150: Luminance processing controls #1 #2 and #3 = %02x %02x %02x\n", - tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_1), - tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_2), - tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_3)); - printk("tvp5150: Brightness control = 0x%02x\n", - tvp5150_read(sd, TVP5150_BRIGHT_CTL)); - printk("tvp5150: Color saturation control = 0x%02x\n", - tvp5150_read(sd, TVP5150_SATURATION_CTL)); - printk("tvp5150: Hue control = 0x%02x\n", - tvp5150_read(sd, TVP5150_HUE_CTL)); - printk("tvp5150: Contrast control = 0x%02x\n", - tvp5150_read(sd, TVP5150_CONTRAST_CTL)); - printk("tvp5150: Outputs and data rates select = 0x%02x\n", - tvp5150_read(sd, TVP5150_DATA_RATE_SEL)); - printk("tvp5150: Configuration shared pins = 0x%02x\n", - tvp5150_read(sd, TVP5150_CONF_SHARED_PIN)); - printk("tvp5150: Active video cropping start = 0x%02x%02x\n", - tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_MSB), - tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_LSB)); - printk("tvp5150: Active video cropping stop = 0x%02x%02x\n", - tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_MSB), - tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_LSB)); - printk("tvp5150: Genlock/RTC = 0x%02x\n", - tvp5150_read(sd, TVP5150_GENLOCK)); - printk("tvp5150: Horizontal sync start = 0x%02x\n", - tvp5150_read(sd, TVP5150_HORIZ_SYNC_START)); - printk("tvp5150: Vertical blanking start = 0x%02x\n", - tvp5150_read(sd, TVP5150_VERT_BLANKING_START)); - printk("tvp5150: Vertical blanking stop = 0x%02x\n", - tvp5150_read(sd, TVP5150_VERT_BLANKING_STOP)); - printk("tvp5150: Chrominance processing control #1 and #2 = %02x %02x\n", - tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_1), - tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_2)); - printk("tvp5150: Interrupt reset register B = 0x%02x\n", - tvp5150_read(sd, TVP5150_INT_RESET_REG_B)); - printk("tvp5150: Interrupt enable register B = 0x%02x\n", - tvp5150_read(sd, TVP5150_INT_ENABLE_REG_B)); - printk("tvp5150: Interrupt configuration register B = 0x%02x\n", - tvp5150_read(sd, TVP5150_INTT_CONFIG_REG_B)); - printk("tvp5150: Video standard = 0x%02x\n", - tvp5150_read(sd, TVP5150_VIDEO_STD)); - printk("tvp5150: Chroma gain factor: Cb=0x%02x Cr=0x%02x\n", - tvp5150_read(sd, TVP5150_CB_GAIN_FACT), - tvp5150_read(sd, TVP5150_CR_GAIN_FACTOR)); - printk("tvp5150: Macrovision on counter = 0x%02x\n", - tvp5150_read(sd, TVP5150_MACROVISION_ON_CTR)); - printk("tvp5150: Macrovision off counter = 0x%02x\n", - tvp5150_read(sd, TVP5150_MACROVISION_OFF_CTR)); - printk("tvp5150: ITU-R BT.656.%d timing(TVP5150AM1 only)\n", - (tvp5150_read(sd, TVP5150_REV_SELECT) & 1) ? 3 : 4); - printk("tvp5150: Device ID = %02x%02x\n", - tvp5150_read(sd, TVP5150_MSB_DEV_ID), - tvp5150_read(sd, TVP5150_LSB_DEV_ID)); - printk("tvp5150: ROM version = (hex) %02x.%02x\n", - tvp5150_read(sd, TVP5150_ROM_MAJOR_VER), - tvp5150_read(sd, TVP5150_ROM_MINOR_VER)); - printk("tvp5150: Vertical line count = 0x%02x%02x\n", - tvp5150_read(sd, TVP5150_VERT_LN_COUNT_MSB), - tvp5150_read(sd, TVP5150_VERT_LN_COUNT_LSB)); - printk("tvp5150: Interrupt status register B = 0x%02x\n", - tvp5150_read(sd, TVP5150_INT_STATUS_REG_B)); - printk("tvp5150: Interrupt active register B = 0x%02x\n", - tvp5150_read(sd, TVP5150_INT_ACTIVE_REG_B)); - printk("tvp5150: Status regs #1 to #5 = %02x %02x %02x %02x %02x\n", - tvp5150_read(sd, TVP5150_STATUS_REG_1), - tvp5150_read(sd, TVP5150_STATUS_REG_2), - tvp5150_read(sd, TVP5150_STATUS_REG_3), - tvp5150_read(sd, TVP5150_STATUS_REG_4), - tvp5150_read(sd, TVP5150_STATUS_REG_5)); + dprintk0(sd->dev, "tvp5150: Video input source selection #1 = 0x%02x\n", + tvp5150_read(sd, TVP5150_VD_IN_SRC_SEL_1)); + dprintk0(sd->dev, "tvp5150: Analog channel controls = 0x%02x\n", + tvp5150_read(sd, TVP5150_ANAL_CHL_CTL)); + dprintk0(sd->dev, "tvp5150: Operation mode controls = 0x%02x\n", + tvp5150_read(sd, TVP5150_OP_MODE_CTL)); + dprintk0(sd->dev, "tvp5150: Miscellaneous controls = 0x%02x\n", + tvp5150_read(sd, TVP5150_MISC_CTL)); + dprintk0(sd->dev, "tvp5150: Autoswitch mask= 0x%02x\n", + tvp5150_read(sd, TVP5150_AUTOSW_MSK)); + dprintk0(sd->dev, "tvp5150: Color killer threshold control = 0x%02x\n", + tvp5150_read(sd, TVP5150_COLOR_KIL_THSH_CTL)); + dprintk0(sd->dev, "tvp5150: Luminance processing controls #1 #2 and #3 = %02x %02x %02x\n", + tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_1), + tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_2), + tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_3)); + dprintk0(sd->dev, "tvp5150: Brightness control = 0x%02x\n", + tvp5150_read(sd, TVP5150_BRIGHT_CTL)); + dprintk0(sd->dev, "tvp5150: Color saturation control = 0x%02x\n", + tvp5150_read(sd, TVP5150_SATURATION_CTL)); + dprintk0(sd->dev, "tvp5150: Hue control = 0x%02x\n", + tvp5150_read(sd, TVP5150_HUE_CTL)); + dprintk0(sd->dev, "tvp5150: Contrast control = 0x%02x\n", + tvp5150_read(sd, TVP5150_CONTRAST_CTL)); + dprintk0(sd->dev, "tvp5150: Outputs and data rates select = 0x%02x\n", + tvp5150_read(sd, TVP5150_DATA_RATE_SEL)); + dprintk0(sd->dev, "tvp5150: Configuration shared pins = 0x%02x\n", + tvp5150_read(sd, TVP5150_CONF_SHARED_PIN)); + dprintk0(sd->dev, "tvp5150: Active video cropping start = 0x%02x%02x\n", + tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_MSB), + tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_LSB)); + dprintk0(sd->dev, "tvp5150: Active video cropping stop = 0x%02x%02x\n", + tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_MSB), + tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_LSB)); + dprintk0(sd->dev, "tvp5150: Genlock/RTC = 0x%02x\n", + tvp5150_read(sd, TVP5150_GENLOCK)); + dprintk0(sd->dev, "tvp5150: Horizontal sync start = 0x%02x\n", + tvp5150_read(sd, TVP5150_HORIZ_SYNC_START)); + dprintk0(sd->dev, "tvp5150: Vertical blanking start = 0x%02x\n", + tvp5150_read(sd, TVP5150_VERT_BLANKING_START)); + dprintk0(sd->dev, "tvp5150: Vertical blanking stop = 0x%02x\n", + tvp5150_read(sd, TVP5150_VERT_BLANKING_STOP)); + dprintk0(sd->dev, "tvp5150: Chrominance processing control #1 and #2 = %02x %02x\n", + tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_1), + tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_2)); + dprintk0(sd->dev, "tvp5150: Interrupt reset register B = 0x%02x\n", + tvp5150_read(sd, TVP5150_INT_RESET_REG_B)); + dprintk0(sd->dev, "tvp5150: Interrupt enable register B = 0x%02x\n", + tvp5150_read(sd, TVP5150_INT_ENABLE_REG_B)); + dprintk0(sd->dev, "tvp5150: Interrupt configuration register B = 0x%02x\n", + tvp5150_read(sd, TVP5150_INTT_CONFIG_REG_B)); + dprintk0(sd->dev, "tvp5150: Video standard = 0x%02x\n", + tvp5150_read(sd, TVP5150_VIDEO_STD)); + dprintk0(sd->dev, "tvp5150: Chroma gain factor: Cb=0x%02x Cr=0x%02x\n", + tvp5150_read(sd, TVP5150_CB_GAIN_FACT), + tvp5150_read(sd, TVP5150_CR_GAIN_FACTOR)); + dprintk0(sd->dev, "tvp5150: Macrovision on counter = 0x%02x\n", + tvp5150_read(sd, TVP5150_MACROVISION_ON_CTR)); + dprintk0(sd->dev, "tvp5150: Macrovision off counter = 0x%02x\n", + tvp5150_read(sd, TVP5150_MACROVISION_OFF_CTR)); + dprintk0(sd->dev, "tvp5150: ITU-R BT.656.%d timing(TVP5150AM1 only)\n", + (tvp5150_read(sd, TVP5150_REV_SELECT) & 1) ? 3 : 4); + dprintk0(sd->dev, "tvp5150: Device ID = %02x%02x\n", + tvp5150_read(sd, TVP5150_MSB_DEV_ID), + tvp5150_read(sd, TVP5150_LSB_DEV_ID)); + dprintk0(sd->dev, "tvp5150: ROM version = (hex) %02x.%02x\n", + tvp5150_read(sd, TVP5150_ROM_MAJOR_VER), + tvp5150_read(sd, TVP5150_ROM_MINOR_VER)); + dprintk0(sd->dev, "tvp5150: Vertical line count = 0x%02x%02x\n", + tvp5150_read(sd, TVP5150_VERT_LN_COUNT_MSB), + tvp5150_read(sd, TVP5150_VERT_LN_COUNT_LSB)); + dprintk0(sd->dev, "tvp5150: Interrupt status register B = 0x%02x\n", + tvp5150_read(sd, TVP5150_INT_STATUS_REG_B)); + dprintk0(sd->dev, "tvp5150: Interrupt active register B = 0x%02x\n", + tvp5150_read(sd, TVP5150_INT_ACTIVE_REG_B)); + dprintk0(sd->dev, "tvp5150: Status regs #1 to #5 = %02x %02x %02x %02x %02x\n", + tvp5150_read(sd, TVP5150_STATUS_REG_1), + tvp5150_read(sd, TVP5150_STATUS_REG_2), + tvp5150_read(sd, TVP5150_STATUS_REG_3), + tvp5150_read(sd, TVP5150_STATUS_REG_4), + tvp5150_read(sd, TVP5150_STATUS_REG_5)); dump_reg_range(sd, "Teletext filter 1", TVP5150_TELETEXT_FIL1_INI, TVP5150_TELETEXT_FIL1_END, 8); dump_reg_range(sd, "Teletext filter 2", TVP5150_TELETEXT_FIL2_INI, TVP5150_TELETEXT_FIL2_END, 8); - printk("tvp5150: Teletext filter enable = 0x%02x\n", - tvp5150_read(sd, TVP5150_TELETEXT_FIL_ENA)); - printk("tvp5150: Interrupt status register A = 0x%02x\n", - tvp5150_read(sd, TVP5150_INT_STATUS_REG_A)); - printk("tvp5150: Interrupt enable register A = 0x%02x\n", - tvp5150_read(sd, TVP5150_INT_ENABLE_REG_A)); - printk("tvp5150: Interrupt configuration = 0x%02x\n", - tvp5150_read(sd, TVP5150_INT_CONF)); - printk("tvp5150: VDP status register = 0x%02x\n", - tvp5150_read(sd, TVP5150_VDP_STATUS_REG)); - printk("tvp5150: FIFO word count = 0x%02x\n", - tvp5150_read(sd, TVP5150_FIFO_WORD_COUNT)); - printk("tvp5150: FIFO interrupt threshold = 0x%02x\n", - tvp5150_read(sd, TVP5150_FIFO_INT_THRESHOLD)); - printk("tvp5150: FIFO reset = 0x%02x\n", - tvp5150_read(sd, TVP5150_FIFO_RESET)); - printk("tvp5150: Line number interrupt = 0x%02x\n", - tvp5150_read(sd, TVP5150_LINE_NUMBER_INT)); - printk("tvp5150: Pixel alignment register = 0x%02x%02x\n", - tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_HIGH), - tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_LOW)); - printk("tvp5150: FIFO output control = 0x%02x\n", - tvp5150_read(sd, TVP5150_FIFO_OUT_CTRL)); - printk("tvp5150: Full field enable = 0x%02x\n", - tvp5150_read(sd, TVP5150_FULL_FIELD_ENA)); - printk("tvp5150: Full field mode register = 0x%02x\n", - tvp5150_read(sd, TVP5150_FULL_FIELD_MODE_REG)); + dprintk0(sd->dev, "tvp5150: Teletext filter enable = 0x%02x\n", + tvp5150_read(sd, TVP5150_TELETEXT_FIL_ENA)); + dprintk0(sd->dev, "tvp5150: Interrupt status register A = 0x%02x\n", + tvp5150_read(sd, TVP5150_INT_STATUS_REG_A)); + dprintk0(sd->dev, "tvp5150: Interrupt enable register A = 0x%02x\n", + tvp5150_read(sd, TVP5150_INT_ENABLE_REG_A)); + dprintk0(sd->dev, "tvp5150: Interrupt configuration = 0x%02x\n", + tvp5150_read(sd, TVP5150_INT_CONF)); + dprintk0(sd->dev, "tvp5150: VDP status register = 0x%02x\n", + tvp5150_read(sd, TVP5150_VDP_STATUS_REG)); + dprintk0(sd->dev, "tvp5150: FIFO word count = 0x%02x\n", + tvp5150_read(sd, TVP5150_FIFO_WORD_COUNT)); + dprintk0(sd->dev, "tvp5150: FIFO interrupt threshold = 0x%02x\n", + tvp5150_read(sd, TVP5150_FIFO_INT_THRESHOLD)); + dprintk0(sd->dev, "tvp5150: FIFO reset = 0x%02x\n", + tvp5150_read(sd, TVP5150_FIFO_RESET)); + dprintk0(sd->dev, "tvp5150: Line number interrupt = 0x%02x\n", + tvp5150_read(sd, TVP5150_LINE_NUMBER_INT)); + dprintk0(sd->dev, "tvp5150: Pixel alignment register = 0x%02x%02x\n", + tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_HIGH), + tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_LOW)); + dprintk0(sd->dev, "tvp5150: FIFO output control = 0x%02x\n", + tvp5150_read(sd, TVP5150_FIFO_OUT_CTRL)); + dprintk0(sd->dev, "tvp5150: Full field enable = 0x%02x\n", + tvp5150_read(sd, TVP5150_FULL_FIELD_ENA)); + dprintk0(sd->dev, "tvp5150: Full field mode register = 0x%02x\n", + tvp5150_read(sd, TVP5150_FULL_FIELD_MODE_REG)); dump_reg_range(sd, "CC data", TVP5150_CC_DATA_INI, TVP5150_CC_DATA_END, 8); @@ -251,20 +276,24 @@ static int tvp5150_log_status(struct v4l2_subdev *sd) Basic functions ****************************************************************************/ -static inline void tvp5150_selmux(struct v4l2_subdev *sd) +static void tvp5150_selmux(struct v4l2_subdev *sd) { int opmode = 0; struct tvp5150 *decoder = to_tvp5150(sd); + unsigned int mask, val; int input = 0; - int val; - if ((decoder->output & TVP5150_BLACK_SCREEN) || !decoder->enable) - input = 8; + /* Only tvp5150am1 and tvp5151 have signal generator support */ + if ((decoder->dev_id == 0x5150 && decoder->rom_ver == 0x0400) || + (decoder->dev_id == 0x5151 && decoder->rom_ver == 0x0100)) { + if (!decoder->enable) + input = 8; + } switch (decoder->input) { case TVP5150_COMPOSITE1: input |= 2; - /* fall through */ + fallthrough; case TVP5150_COMPOSITE0: break; case TVP5150_SVIDEO: @@ -273,28 +302,29 @@ static inline void tvp5150_selmux(struct v4l2_subdev *sd) break; } - v4l2_dbg(1, debug, sd, "Selecting video route: route input=%i, output=%i " - "=> tvp5150 input=%i, opmode=%i\n", - decoder->input, decoder->output, - input, opmode); - - tvp5150_write(sd, TVP5150_OP_MODE_CTL, opmode); - tvp5150_write(sd, TVP5150_VD_IN_SRC_SEL_1, input); - - /* Svideo should enable YCrCb output and disable GPCL output - * For Composite and TV, it should be the reverse + dev_dbg_lvl(sd->dev, 1, debug, + "Selecting video route: route input=%s, output=%s => tvp5150 input=0x%02x, opmode=0x%02x\n", + decoder->input == 0 ? "aip1a" : + decoder->input == 2 ? "aip1b" : "svideo", + decoder->output == 0 ? "normal" : "black-frame-gen", + input, opmode); + + regmap_write(decoder->regmap, TVP5150_OP_MODE_CTL, opmode); + regmap_write(decoder->regmap, TVP5150_VD_IN_SRC_SEL_1, input); + + /* + * Setup the FID/GLCO/VLK/HVLK and INTREQ/GPCL/VBLK output signals. For + * S-Video we output the vertical lock (VLK) signal on FID/GLCO/VLK/HVLK + * and set INTREQ/GPCL/VBLK to logic 0. For composite we output the + * field indicator (FID) signal on FID/GLCO/VLK/HVLK and set + * INTREQ/GPCL/VBLK to logic 1. */ - val = tvp5150_read(sd, TVP5150_MISC_CTL); - if (val < 0) { - v4l2_err(sd, "%s: failed with error = %d\n", __func__, val); - return; - } - + mask = TVP5150_MISC_CTL_GPCL | TVP5150_MISC_CTL_HVLK; if (decoder->input == TVP5150_SVIDEO) - val = (val & ~0x40) | 0x10; + val = TVP5150_MISC_CTL_HVLK; else - val = (val & ~0x10) | 0x40; - tvp5150_write(sd, TVP5150_MISC_CTL, val); + val = TVP5150_MISC_CTL_GPCL; + regmap_update_bits(decoder->regmap, TVP5150_MISC_CTL, mask, val); }; struct i2c_reg_value { @@ -305,159 +335,162 @@ struct i2c_reg_value { /* Default values as sugested at TVP5150AM1 datasheet */ static const struct i2c_reg_value tvp5150_init_default[] = { { /* 0x00 */ - TVP5150_VD_IN_SRC_SEL_1,0x00 + TVP5150_VD_IN_SRC_SEL_1, 0x00 }, { /* 0x01 */ - TVP5150_ANAL_CHL_CTL,0x15 + TVP5150_ANAL_CHL_CTL, 0x15 }, { /* 0x02 */ - TVP5150_OP_MODE_CTL,0x00 + TVP5150_OP_MODE_CTL, 0x00 }, { /* 0x03 */ - TVP5150_MISC_CTL,0x01 + TVP5150_MISC_CTL, 0x01 }, { /* 0x06 */ - TVP5150_COLOR_KIL_THSH_CTL,0x10 + TVP5150_COLOR_KIL_THSH_CTL, 0x10 }, { /* 0x07 */ - TVP5150_LUMA_PROC_CTL_1,0x60 + TVP5150_LUMA_PROC_CTL_1, 0x60 }, { /* 0x08 */ - TVP5150_LUMA_PROC_CTL_2,0x00 + TVP5150_LUMA_PROC_CTL_2, 0x00 }, { /* 0x09 */ - TVP5150_BRIGHT_CTL,0x80 + TVP5150_BRIGHT_CTL, 0x80 }, { /* 0x0a */ - TVP5150_SATURATION_CTL,0x80 + TVP5150_SATURATION_CTL, 0x80 }, { /* 0x0b */ - TVP5150_HUE_CTL,0x00 + TVP5150_HUE_CTL, 0x00 }, { /* 0x0c */ - TVP5150_CONTRAST_CTL,0x80 + TVP5150_CONTRAST_CTL, 0x80 }, { /* 0x0d */ - TVP5150_DATA_RATE_SEL,0x47 + TVP5150_DATA_RATE_SEL, 0x47 }, { /* 0x0e */ - TVP5150_LUMA_PROC_CTL_3,0x00 + TVP5150_LUMA_PROC_CTL_3, 0x00 }, { /* 0x0f */ - TVP5150_CONF_SHARED_PIN,0x08 + TVP5150_CONF_SHARED_PIN, 0x08 }, { /* 0x11 */ - TVP5150_ACT_VD_CROP_ST_MSB,0x00 + TVP5150_ACT_VD_CROP_ST_MSB, 0x00 }, { /* 0x12 */ - TVP5150_ACT_VD_CROP_ST_LSB,0x00 + TVP5150_ACT_VD_CROP_ST_LSB, 0x00 }, { /* 0x13 */ - TVP5150_ACT_VD_CROP_STP_MSB,0x00 + TVP5150_ACT_VD_CROP_STP_MSB, 0x00 }, { /* 0x14 */ - TVP5150_ACT_VD_CROP_STP_LSB,0x00 + TVP5150_ACT_VD_CROP_STP_LSB, 0x00 }, { /* 0x15 */ - TVP5150_GENLOCK,0x01 + TVP5150_GENLOCK, 0x01 }, { /* 0x16 */ - TVP5150_HORIZ_SYNC_START,0x80 + TVP5150_HORIZ_SYNC_START, 0x80 }, { /* 0x18 */ - TVP5150_VERT_BLANKING_START,0x00 + TVP5150_VERT_BLANKING_START, 0x00 }, { /* 0x19 */ - TVP5150_VERT_BLANKING_STOP,0x00 + TVP5150_VERT_BLANKING_STOP, 0x00 }, { /* 0x1a */ - TVP5150_CHROMA_PROC_CTL_1,0x0c + TVP5150_CHROMA_PROC_CTL_1, 0x0c }, { /* 0x1b */ - TVP5150_CHROMA_PROC_CTL_2,0x14 + TVP5150_CHROMA_PROC_CTL_2, 0x14 }, { /* 0x1c */ - TVP5150_INT_RESET_REG_B,0x00 + TVP5150_INT_RESET_REG_B, 0x00 }, { /* 0x1d */ - TVP5150_INT_ENABLE_REG_B,0x00 + TVP5150_INT_ENABLE_REG_B, 0x00 }, { /* 0x1e */ - TVP5150_INTT_CONFIG_REG_B,0x00 + TVP5150_INTT_CONFIG_REG_B, 0x00 }, { /* 0x28 */ - TVP5150_VIDEO_STD,0x00 + TVP5150_VIDEO_STD, 0x00 }, { /* 0x2e */ - TVP5150_MACROVISION_ON_CTR,0x0f + TVP5150_MACROVISION_ON_CTR, 0x0f }, { /* 0x2f */ - TVP5150_MACROVISION_OFF_CTR,0x01 + TVP5150_MACROVISION_OFF_CTR, 0x01 }, { /* 0xbb */ - TVP5150_TELETEXT_FIL_ENA,0x00 + TVP5150_TELETEXT_FIL_ENA, 0x00 }, { /* 0xc0 */ - TVP5150_INT_STATUS_REG_A,0x00 + TVP5150_INT_STATUS_REG_A, 0x00 }, { /* 0xc1 */ - TVP5150_INT_ENABLE_REG_A,0x00 + TVP5150_INT_ENABLE_REG_A, 0x00 }, { /* 0xc2 */ - TVP5150_INT_CONF,0x04 + TVP5150_INT_CONF, 0x04 }, { /* 0xc8 */ - TVP5150_FIFO_INT_THRESHOLD,0x80 + TVP5150_FIFO_INT_THRESHOLD, 0x80 }, { /* 0xc9 */ - TVP5150_FIFO_RESET,0x00 + TVP5150_FIFO_RESET, 0x00 }, { /* 0xca */ - TVP5150_LINE_NUMBER_INT,0x00 + TVP5150_LINE_NUMBER_INT, 0x00 }, { /* 0xcb */ - TVP5150_PIX_ALIGN_REG_LOW,0x4e + TVP5150_PIX_ALIGN_REG_LOW, 0x4e }, { /* 0xcc */ - TVP5150_PIX_ALIGN_REG_HIGH,0x00 + TVP5150_PIX_ALIGN_REG_HIGH, 0x00 }, { /* 0xcd */ - TVP5150_FIFO_OUT_CTRL,0x01 + TVP5150_FIFO_OUT_CTRL, 0x01 }, { /* 0xcf */ - TVP5150_FULL_FIELD_ENA,0x00 + TVP5150_FULL_FIELD_ENA, 0x00 }, { /* 0xd0 */ - TVP5150_LINE_MODE_INI,0x00 + TVP5150_LINE_MODE_INI, 0x00 }, { /* 0xfc */ - TVP5150_FULL_FIELD_MODE_REG,0x7f + TVP5150_FULL_FIELD_MODE_REG, 0x7f }, { /* end of data */ - 0xff,0xff + 0xff, 0xff } }; /* Default values as sugested at TVP5150AM1 datasheet */ static const struct i2c_reg_value tvp5150_init_enable[] = { - { - TVP5150_CONF_SHARED_PIN, 2 - },{ /* Automatic offset and AGC enabled */ + { /* Automatic offset and AGC enabled */ TVP5150_ANAL_CHL_CTL, 0x15 - },{ /* Activate YCrCb output 0x9 or 0xd ? */ - TVP5150_MISC_CTL, 0x6f - },{ /* Activates video std autodetection for all standards */ + }, { /* Activate YCrCb output 0x9 or 0xd ? */ + TVP5150_MISC_CTL, TVP5150_MISC_CTL_GPCL | + TVP5150_MISC_CTL_INTREQ_OE | + TVP5150_MISC_CTL_YCBCR_OE | + TVP5150_MISC_CTL_SYNC_OE | + TVP5150_MISC_CTL_VBLANK | + TVP5150_MISC_CTL_CLOCK_OE, + }, { /* Activates video std autodetection for all standards */ TVP5150_AUTOSW_MSK, 0x0 - },{ /* Default format: 0x47. For 4:2:2: 0x40 */ + }, { /* Default format: 0x47. For 4:2:2: 0x40 */ TVP5150_DATA_RATE_SEL, 0x47 - },{ + }, { TVP5150_CHROMA_PROC_CTL_1, 0x0c - },{ + }, { TVP5150_CHROMA_PROC_CTL_2, 0x54 - },{ /* Non documented, but initialized on WinTV USB2 */ + }, { /* Non documented, but initialized on WinTV USB2 */ 0x27, 0x20 - },{ - 0xff,0xff + }, { + 0xff, 0xff } }; @@ -481,118 +514,125 @@ struct i2c_vbi_ram_value { * and so on. There are 16 possible locations from 0 to 15. */ -static struct i2c_vbi_ram_value vbi_ram_default[] = -{ - /* FIXME: Current api doesn't handle all VBI types, those not - yet supported are placed under #if 0 */ +static const struct i2c_vbi_ram_value vbi_ram_default[] = { + + /* + * FIXME: Current api doesn't handle all VBI types, those not + * yet supported are placed under #if 0 + */ #if 0 - {0x010, /* Teletext, SECAM, WST System A */ - {V4L2_SLICED_TELETEXT_SECAM,6,23,1}, + [0] = {0x010, /* Teletext, SECAM, WST System A */ + {V4L2_SLICED_TELETEXT_SECAM, 6, 23, 1}, { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x26, 0xe6, 0xb4, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00 } }, #endif - {0x030, /* Teletext, PAL, WST System B */ - {V4L2_SLICED_TELETEXT_B,6,22,1}, + [1] = {0x030, /* Teletext, PAL, WST System B */ + {V4L2_SLICED_TELETEXT_B, 6, 22, 1}, { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x2b, 0xa6, 0x72, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00 } }, #if 0 - {0x050, /* Teletext, PAL, WST System C */ - {V4L2_SLICED_TELETEXT_PAL_C,6,22,1}, + [2] = {0x050, /* Teletext, PAL, WST System C */ + {V4L2_SLICED_TELETEXT_PAL_C, 6, 22, 1}, { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22, 0xa6, 0x98, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } }, - {0x070, /* Teletext, NTSC, WST System B */ - {V4L2_SLICED_TELETEXT_NTSC_B,10,21,1}, + [3] = {0x070, /* Teletext, NTSC, WST System B */ + {V4L2_SLICED_TELETEXT_NTSC_B, 10, 21, 1}, { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x23, 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } }, - {0x090, /* Tetetext, NTSC NABTS System C */ - {V4L2_SLICED_TELETEXT_NTSC_C,10,21,1}, + [4] = {0x090, /* Tetetext, NTSC NABTS System C */ + {V4L2_SLICED_TELETEXT_NTSC_C, 10, 21, 1}, { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22, 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x15, 0x00 } }, - {0x0b0, /* Teletext, NTSC-J, NABTS System D */ - {V4L2_SLICED_TELETEXT_NTSC_D,10,21,1}, + [5] = {0x0b0, /* Teletext, NTSC-J, NABTS System D */ + {V4L2_SLICED_TELETEXT_NTSC_D, 10, 21, 1}, { 0xaa, 0xaa, 0xff, 0xff, 0xa7, 0x2e, 0x20, 0x23, 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } }, - {0x0d0, /* Closed Caption, PAL/SECAM */ - {V4L2_SLICED_CAPTION_625,22,22,1}, + [6] = {0x0d0, /* Closed Caption, PAL/SECAM */ + {V4L2_SLICED_CAPTION_625, 22, 22, 1}, { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02, 0xa6, 0x7b, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 } }, #endif - {0x0f0, /* Closed Caption, NTSC */ - {V4L2_SLICED_CAPTION_525,21,21,1}, + [7] = {0x0f0, /* Closed Caption, NTSC */ + {V4L2_SLICED_CAPTION_525, 21, 21, 1}, { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02, 0x69, 0x8c, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 } }, - {0x110, /* Wide Screen Signal, PAL/SECAM */ - {V4L2_SLICED_WSS_625,23,23,1}, + [8] = {0x110, /* Wide Screen Signal, PAL/SECAM */ + {V4L2_SLICED_WSS_625, 23, 23, 1}, { 0x5b, 0x55, 0xc5, 0xff, 0x00, 0x71, 0x6e, 0x42, 0xa6, 0xcd, 0x0f, 0x00, 0x00, 0x00, 0x3a, 0x00 } }, #if 0 - {0x130, /* Wide Screen Signal, NTSC C */ - {V4L2_SLICED_WSS_525,20,20,1}, + [9] = {0x130, /* Wide Screen Signal, NTSC C */ + {V4L2_SLICED_WSS_525, 20, 20, 1}, { 0x38, 0x00, 0x3f, 0x00, 0x00, 0x71, 0x6e, 0x43, 0x69, 0x7c, 0x08, 0x00, 0x00, 0x00, 0x39, 0x00 } }, - {0x150, /* Vertical Interval Timecode (VITC), PAL/SECAM */ - {V4l2_SLICED_VITC_625,6,22,0}, + [10] = {0x150, /* Vertical Interval Timecode (VITC), PAL/SECAM */ + {V4l2_SLICED_VITC_625, 6, 22, 0}, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49, 0xa6, 0x85, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 } }, - {0x170, /* Vertical Interval Timecode (VITC), NTSC */ - {V4l2_SLICED_VITC_525,10,20,0}, + [11] = {0x170, /* Vertical Interval Timecode (VITC), NTSC */ + {V4l2_SLICED_VITC_525, 10, 20, 0}, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49, 0x69, 0x94, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 } }, #endif - {0x190, /* Video Program System (VPS), PAL */ - {V4L2_SLICED_VPS,16,16,0}, + [12] = {0x190, /* Video Program System (VPS), PAL */ + {V4L2_SLICED_VPS, 16, 16, 0}, { 0xaa, 0xaa, 0xff, 0xff, 0xba, 0xce, 0x2b, 0x0d, 0xa6, 0xda, 0x0b, 0x00, 0x00, 0x00, 0x60, 0x00 } }, /* 0x1d0 User programmable */ - - /* End of struct */ - { (u16)-1 } }; static int tvp5150_write_inittab(struct v4l2_subdev *sd, const struct i2c_reg_value *regs) { + struct tvp5150 *decoder = to_tvp5150(sd); + while (regs->reg != 0xff) { - tvp5150_write(sd, regs->reg, regs->value); + regmap_write(decoder->regmap, regs->reg, regs->value); regs++; } return 0; } -static int tvp5150_vdp_init(struct v4l2_subdev *sd, - const struct i2c_vbi_ram_value *regs) +static int tvp5150_vdp_init(struct v4l2_subdev *sd) { + struct tvp5150 *decoder = to_tvp5150(sd); + struct regmap *map = decoder->regmap; unsigned int i; + int j; /* Disable Full Field */ - tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0); + regmap_write(map, TVP5150_FULL_FIELD_ENA, 0); /* Before programming, Line mode should be at 0xff */ for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++) - tvp5150_write(sd, i, 0xff); + regmap_write(map, i, 0xff); /* Load Ram Table */ - while (regs->reg != (u16)-1) { - tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_HIGH, regs->reg >> 8); - tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_LOW, regs->reg); + for (j = 0; j < ARRAY_SIZE(vbi_ram_default); j++) { + const struct i2c_vbi_ram_value *regs = &vbi_ram_default[j]; - for (i = 0; i < 16; i++) - tvp5150_write(sd, TVP5150_VDP_CONF_RAM_DATA, regs->values[i]); + if (!regs->type.vbi_type) + continue; - regs++; + regmap_write(map, TVP5150_CONF_RAM_ADDR_HIGH, regs->reg >> 8); + regmap_write(map, TVP5150_CONF_RAM_ADDR_LOW, regs->reg); + + for (i = 0; i < 16; i++) + regmap_write(map, TVP5150_VDP_CONF_RAM_DATA, + regs->values[i]); } return 0; } @@ -601,19 +641,23 @@ static int tvp5150_vdp_init(struct v4l2_subdev *sd, static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_cap *cap) { - const struct i2c_vbi_ram_value *regs = vbi_ram_default; - int line; + int line, i; + + dev_dbg_lvl(sd->dev, 1, debug, "g_sliced_vbi_cap\n"); + memset(cap, 0, sizeof(*cap)); + + for (i = 0; i < ARRAY_SIZE(vbi_ram_default); i++) { + const struct i2c_vbi_ram_value *regs = &vbi_ram_default[i]; - v4l2_dbg(1, debug, sd, "g_sliced_vbi_cap\n"); - memset(cap, 0, sizeof *cap); + if (!regs->type.vbi_type) + continue; - while (regs->reg != (u16)-1 ) { - for (line=regs->type.ini_line;line<=regs->type.end_line;line++) { + for (line = regs->type.ini_line; + line <= regs->type.end_line; + line++) { cap->service_lines[0][line] |= regs->type.vbi_type; } cap->service_set |= regs->type.vbi_type; - - regs++; } return 0; } @@ -632,56 +676,51 @@ static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd, * MSB = field2 */ static int tvp5150_set_vbi(struct v4l2_subdev *sd, - const struct i2c_vbi_ram_value *regs, - unsigned int type,u8 flags, int line, + unsigned int type, u8 flags, int line, const int fields) { struct tvp5150 *decoder = to_tvp5150(sd); v4l2_std_id std = decoder->norm; u8 reg; - int pos=0; + int i, pos = 0; if (std == V4L2_STD_ALL) { - v4l2_err(sd, "VBI can't be configured without knowing number of lines\n"); + dev_err(sd->dev, "VBI can't be configured without knowing number of lines\n"); return 0; } else if (std & V4L2_STD_625_50) { /* Don't follow NTSC Line number convension */ line += 3; } - if (line<6||line>27) + if (line < 6 || line > 27) return 0; - while (regs->reg != (u16)-1 ) { + for (i = 0; i < ARRAY_SIZE(vbi_ram_default); i++) { + const struct i2c_vbi_ram_value *regs = &vbi_ram_default[i]; + + if (!regs->type.vbi_type) + continue; + if ((type & regs->type.vbi_type) && - (line>=regs->type.ini_line) && - (line<=regs->type.end_line)) { - type=regs->type.vbi_type; + (line >= regs->type.ini_line) && + (line <= regs->type.end_line)) break; - } - - regs++; pos++; } - if (regs->reg == (u16)-1) - return 0; - type=pos | (flags & 0xf0); - reg=((line-6)<<1)+TVP5150_LINE_MODE_INI; + type = pos | (flags & 0xf0); + reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI; - if (fields&1) { - tvp5150_write(sd, reg, type); - } + if (fields & 1) + regmap_write(decoder->regmap, reg, type); - if (fields&2) { - tvp5150_write(sd, reg+1, type); - } + if (fields & 2) + regmap_write(decoder->regmap, reg + 1, type); return type; } -static int tvp5150_get_vbi(struct v4l2_subdev *sd, - const struct i2c_vbi_ram_value *regs, int line) +static int tvp5150_get_vbi(struct v4l2_subdev *sd, int line) { struct tvp5150 *decoder = to_tvp5150(sd); v4l2_std_id std = decoder->norm; @@ -690,7 +729,7 @@ static int tvp5150_get_vbi(struct v4l2_subdev *sd, int i, ret = 0; if (std == V4L2_STD_ALL) { - v4l2_err(sd, "VBI can't be configured without knowing number of lines\n"); + dev_err(sd->dev, "VBI can't be configured without knowing number of lines\n"); return 0; } else if (std & V4L2_STD_625_50) { /* Don't follow NTSC Line number convension */ @@ -705,13 +744,13 @@ static int tvp5150_get_vbi(struct v4l2_subdev *sd, for (i = 0; i <= 1; i++) { ret = tvp5150_read(sd, reg + i); if (ret < 0) { - v4l2_err(sd, "%s: failed with error = %d\n", + dev_err(sd->dev, "%s: failed with error = %d\n", __func__, ret); return 0; } pos = ret & 0x0f; - if (pos < 0x0f) - type |= regs[pos].type.vbi_type; + if (pos < ARRAY_SIZE(vbi_ram_default)) + type |= vbi_ram_default[pos].type.vbi_type; } return type; @@ -722,8 +761,6 @@ static int tvp5150_set_std(struct v4l2_subdev *sd, v4l2_std_id std) struct tvp5150 *decoder = to_tvp5150(sd); int fmt = 0; - decoder->norm = std; - /* First tests should be against specific std */ if (std == V4L2_STD_NTSC_443) { @@ -742,222 +779,698 @@ static int tvp5150_set_std(struct v4l2_subdev *sd, v4l2_std_id std) fmt = VIDEO_STD_SECAM_BIT; } - v4l2_dbg(1, debug, sd, "Set video std register to %d.\n", fmt); - tvp5150_write(sd, TVP5150_VIDEO_STD, fmt); + dev_dbg_lvl(sd->dev, 1, debug, "Set video std register to %d.\n", fmt); + regmap_write(decoder->regmap, TVP5150_VIDEO_STD, fmt); + return 0; +} + +static int tvp5150_g_std(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + + *std = decoder->norm; + return 0; } static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std) { struct tvp5150 *decoder = to_tvp5150(sd); + struct tvp5150_connector *cur_con = decoder->cur_connector; + v4l2_std_id supported_stds; if (decoder->norm == std) return 0; + /* In case of no of-connectors are available no limitations are made */ + if (!decoder->connectors_num) + supported_stds = V4L2_STD_ALL; + else + supported_stds = cur_con->base.connector.analog.sdtv_stds; + + /* + * Check if requested std or group of std's is/are supported by the + * connector. + */ + if ((supported_stds & std) == 0) + return -EINVAL; + /* Change cropping height limits */ if (std & V4L2_STD_525_60) decoder->rect.height = TVP5150_V_MAX_525_60; else decoder->rect.height = TVP5150_V_MAX_OTHERS; + /* Set only the specific supported std in case of group of std's. */ + decoder->norm = supported_stds & std; return tvp5150_set_std(sd, std); } +static v4l2_std_id tvp5150_read_std(struct v4l2_subdev *sd) +{ + int val = tvp5150_read(sd, TVP5150_STATUS_REG_5); + + switch (val & 0x0F) { + case 0x01: + return V4L2_STD_NTSC; + case 0x03: + return V4L2_STD_PAL; + case 0x05: + return V4L2_STD_PAL_M; + case 0x07: + return V4L2_STD_PAL_N | V4L2_STD_PAL_Nc; + case 0x09: + return V4L2_STD_NTSC_443; + case 0xb: + return V4L2_STD_SECAM; + default: + return V4L2_STD_UNKNOWN; + } +} + +static int query_lock(struct v4l2_subdev *sd) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + int status; + + if (decoder->irq) + return decoder->lock; + + regmap_read(decoder->regmap, TVP5150_STATUS_REG_1, &status); + + /* For standard detection, we need the 3 locks */ + return (status & 0x0e) == 0x0e; +} + +static int tvp5150_querystd(struct v4l2_subdev *sd, v4l2_std_id *std_id) +{ + *std_id = query_lock(sd) ? tvp5150_read_std(sd) : V4L2_STD_UNKNOWN; + + return 0; +} + +static const struct v4l2_event tvp5150_ev_fmt = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, +}; + +static irqreturn_t tvp5150_isr(int irq, void *dev_id) +{ + struct tvp5150 *decoder = dev_id; + struct regmap *map = decoder->regmap; + unsigned int mask, active = 0, status = 0; + + mask = TVP5150_MISC_CTL_YCBCR_OE | TVP5150_MISC_CTL_SYNC_OE | + TVP5150_MISC_CTL_CLOCK_OE; + + regmap_read(map, TVP5150_INT_STATUS_REG_A, &status); + if (status) { + regmap_write(map, TVP5150_INT_STATUS_REG_A, status); + + if (status & TVP5150_INT_A_LOCK) { + decoder->lock = !!(status & TVP5150_INT_A_LOCK_STATUS); + dev_dbg_lvl(decoder->sd.dev, 1, debug, + "sync lo%s signal\n", + decoder->lock ? "ck" : "ss"); + v4l2_subdev_notify_event(&decoder->sd, &tvp5150_ev_fmt); + regmap_update_bits(map, TVP5150_MISC_CTL, mask, + decoder->lock ? decoder->oe : 0); + } + + return IRQ_HANDLED; + } + + regmap_read(map, TVP5150_INT_ACTIVE_REG_B, &active); + if (active) { + status = 0; + regmap_read(map, TVP5150_INT_STATUS_REG_B, &status); + if (status) + regmap_write(map, TVP5150_INT_RESET_REG_B, status); + } + + return IRQ_HANDLED; +} + static int tvp5150_reset(struct v4l2_subdev *sd, u32 val) { struct tvp5150 *decoder = to_tvp5150(sd); + struct regmap *map = decoder->regmap; /* Initializes TVP5150 to its default values */ tvp5150_write_inittab(sd, tvp5150_init_default); + if (decoder->irq) { + /* Configure pins: FID, VSYNC, INTREQ, SCLK */ + regmap_write(map, TVP5150_CONF_SHARED_PIN, 0x0); + /* Set interrupt polarity to active high */ + regmap_write(map, TVP5150_INT_CONF, TVP5150_VDPOE | 0x1); + regmap_write(map, TVP5150_INTT_CONFIG_REG_B, 0x1); + } else { + /* Configure pins: FID, VSYNC, GPCL/VBLK, SCLK */ + regmap_write(map, TVP5150_CONF_SHARED_PIN, 0x2); + /* Keep interrupt polarity active low */ + regmap_write(map, TVP5150_INT_CONF, TVP5150_VDPOE); + regmap_write(map, TVP5150_INTT_CONFIG_REG_B, 0x0); + } + /* Initializes VDP registers */ - tvp5150_vdp_init(sd, vbi_ram_default); + tvp5150_vdp_init(sd); /* Selects decoder input */ tvp5150_selmux(sd); + /* Initialize image preferences */ + v4l2_ctrl_handler_setup(&decoder->hdl); + + return 0; +} + +static int tvp5150_enable(struct v4l2_subdev *sd) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + v4l2_std_id std; + /* Initializes TVP5150 to stream enabled values */ tvp5150_write_inittab(sd, tvp5150_init_enable); - /* Initialize image preferences */ - v4l2_ctrl_handler_setup(&decoder->hdl); + if (decoder->norm == V4L2_STD_ALL) + std = tvp5150_read_std(sd); + else + std = decoder->norm; + + /* Disable autoswitch mode */ + tvp5150_set_std(sd, std); + + /* + * Enable the YCbCr and clock outputs. In discrete sync mode + * (non-BT.656) additionally enable the sync outputs. + */ + switch (decoder->mbus_type) { + case V4L2_MBUS_PARALLEL: + /* 8-bit 4:2:2 YUV with discrete sync output */ + regmap_update_bits(decoder->regmap, TVP5150_DATA_RATE_SEL, + 0x7, 0x0); + decoder->oe = TVP5150_MISC_CTL_YCBCR_OE | + TVP5150_MISC_CTL_CLOCK_OE | + TVP5150_MISC_CTL_SYNC_OE; + break; + case V4L2_MBUS_BT656: + decoder->oe = TVP5150_MISC_CTL_YCBCR_OE | + TVP5150_MISC_CTL_CLOCK_OE; + break; + default: + return -EINVAL; + } - tvp5150_set_std(sd, decoder->norm); return 0; }; static int tvp5150_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = to_sd(ctrl); + struct tvp5150 *decoder = to_tvp5150(sd); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - tvp5150_write(sd, TVP5150_BRIGHT_CTL, ctrl->val); + regmap_write(decoder->regmap, TVP5150_BRIGHT_CTL, ctrl->val); return 0; case V4L2_CID_CONTRAST: - tvp5150_write(sd, TVP5150_CONTRAST_CTL, ctrl->val); + regmap_write(decoder->regmap, TVP5150_CONTRAST_CTL, ctrl->val); return 0; case V4L2_CID_SATURATION: - tvp5150_write(sd, TVP5150_SATURATION_CTL, ctrl->val); + regmap_write(decoder->regmap, TVP5150_SATURATION_CTL, + ctrl->val); return 0; case V4L2_CID_HUE: - tvp5150_write(sd, TVP5150_HUE_CTL, ctrl->val); + regmap_write(decoder->regmap, TVP5150_HUE_CTL, ctrl->val); + return 0; + case V4L2_CID_TEST_PATTERN: + decoder->enable = ctrl->val ? false : true; + tvp5150_selmux(sd); return 0; } return -EINVAL; } -static v4l2_std_id tvp5150_read_std(struct v4l2_subdev *sd) +static void tvp5150_set_default(v4l2_std_id std, struct v4l2_rect *crop) { - int val = tvp5150_read(sd, TVP5150_STATUS_REG_5); + /* Default is no cropping */ + crop->top = 0; + crop->left = 0; + crop->width = TVP5150_H_MAX; + if (std & V4L2_STD_525_60) + crop->height = TVP5150_V_MAX_525_60; + else + crop->height = TVP5150_V_MAX_OTHERS; +} - switch (val & 0x0F) { - case 0x01: - return V4L2_STD_NTSC; - case 0x03: - return V4L2_STD_PAL; - case 0x05: - return V4L2_STD_PAL_M; - case 0x07: - return V4L2_STD_PAL_N | V4L2_STD_PAL_Nc; - case 0x09: - return V4L2_STD_NTSC_443; - case 0xb: - return V4L2_STD_SECAM; +static struct v4l2_rect * +tvp5150_get_pad_crop(struct tvp5150 *decoder, + struct v4l2_subdev_state *sd_state, unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &decoder->rect; + case V4L2_SUBDEV_FORMAT_TRY: +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) + return v4l2_subdev_state_get_crop(sd_state, pad); +#else + return ERR_PTR(-EINVAL); +#endif default: - return V4L2_STD_UNKNOWN; + return ERR_PTR(-EINVAL); } } -static int tvp5150_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, - enum v4l2_mbus_pixelcode *code) +static int tvp5150_fill_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *format) { - if (index) + struct v4l2_mbus_framefmt *f; + struct tvp5150 *decoder = to_tvp5150(sd); + + if (!format || (format->pad != TVP5150_PAD_VID_OUT)) return -EINVAL; - *code = V4L2_MBUS_FMT_UYVY8_2X8; + f = &format->format; + + f->width = decoder->rect.width; + f->height = decoder->rect.height / 2; + + f->code = TVP5150_MBUS_FMT; + f->field = TVP5150_FIELD; + f->colorspace = TVP5150_COLORSPACE; + + dev_dbg_lvl(sd->dev, 1, debug, "width = %d, height = %d\n", f->width, + f->height); return 0; } -static int tvp5150_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *f) +static unsigned int tvp5150_get_hmax(struct v4l2_subdev *sd) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + v4l2_std_id std; + + /* Calculate height based on current standard */ + if (decoder->norm == V4L2_STD_ALL) + std = tvp5150_read_std(sd); + else + std = decoder->norm; + + return (std & V4L2_STD_525_60) ? + TVP5150_V_MAX_525_60 : TVP5150_V_MAX_OTHERS; +} + +static void tvp5150_set_hw_selection(struct v4l2_subdev *sd, + struct v4l2_rect *rect) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + unsigned int hmax = tvp5150_get_hmax(sd); + + regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_START, rect->top); + regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_STOP, + rect->top + rect->height - hmax); + regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_MSB, + rect->left >> TVP5150_CROP_SHIFT); + regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_LSB, + rect->left | (1 << TVP5150_CROP_SHIFT)); + regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_MSB, + (rect->left + rect->width - TVP5150_MAX_CROP_LEFT) >> + TVP5150_CROP_SHIFT); + regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_LSB, + rect->left + rect->width - TVP5150_MAX_CROP_LEFT); +} + +static int tvp5150_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) { struct tvp5150 *decoder = to_tvp5150(sd); + struct v4l2_rect *rect = &sel->r; + struct v4l2_rect *crop; + unsigned int hmax; - if (f == NULL) + if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - tvp5150_reset(sd, 0); + dev_dbg_lvl(sd->dev, 1, debug, "%s left=%d, top=%d, width=%d, height=%d\n", + __func__, rect->left, rect->top, rect->width, rect->height); - f->width = decoder->rect.width; - f->height = decoder->rect.height; + /* tvp5150 has some special limits */ + rect->left = clamp(rect->left, 0, TVP5150_MAX_CROP_LEFT); + rect->top = clamp(rect->top, 0, TVP5150_MAX_CROP_TOP); + hmax = tvp5150_get_hmax(sd); + + /* + * alignments: + * - width = 2 due to UYVY colorspace + * - height, image = no special alignment + */ + v4l_bound_align_image(&rect->width, + TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect->left, + TVP5150_H_MAX - rect->left, 1, &rect->height, + hmax - TVP5150_MAX_CROP_TOP - rect->top, + hmax - rect->top, 0, 0); + + if (!IS_ENABLED(CONFIG_VIDEO_V4L2_SUBDEV_API) && + sel->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; - f->code = V4L2_MBUS_FMT_UYVY8_2X8; - f->field = V4L2_FIELD_SEQ_TB; - f->colorspace = V4L2_COLORSPACE_SMPTE170M; + crop = tvp5150_get_pad_crop(decoder, sd_state, sel->pad, sel->which); + if (IS_ERR(crop)) + return PTR_ERR(crop); + + /* + * Update output image size if the selection (crop) rectangle size or + * position has been modified. + */ + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE && + !v4l2_rect_equal(rect, crop)) + tvp5150_set_hw_selection(sd, rect); + + *crop = *rect; - v4l2_dbg(1, debug, sd, "width = %d, height = %d\n", f->width, - f->height); return 0; } -static int tvp5150_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) +static int tvp5150_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) { - struct v4l2_rect rect = a->c; - struct tvp5150 *decoder = to_tvp5150(sd); + struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd); + struct v4l2_rect *crop; v4l2_std_id std; - int hmax; - - v4l2_dbg(1, debug, sd, "%s left=%d, top=%d, width=%d, height=%d\n", - __func__, rect.left, rect.top, rect.width, rect.height); - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = TVP5150_H_MAX; + + /* Calculate height based on current standard */ + if (decoder->norm == V4L2_STD_ALL) + std = tvp5150_read_std(sd); + else + std = decoder->norm; + if (std & V4L2_STD_525_60) + sel->r.height = TVP5150_V_MAX_525_60; + else + sel->r.height = TVP5150_V_MAX_OTHERS; + return 0; + case V4L2_SEL_TGT_CROP: + crop = tvp5150_get_pad_crop(decoder, sd_state, sel->pad, + sel->which); + if (IS_ERR(crop)) + return PTR_ERR(crop); + sel->r = *crop; + return 0; + default: return -EINVAL; + } +} - /* tvp5150 has some special limits */ - rect.left = clamp(rect.left, 0, TVP5150_MAX_CROP_LEFT); - rect.width = clamp(rect.width, - TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left, - TVP5150_H_MAX - rect.left); - rect.top = clamp(rect.top, 0, TVP5150_MAX_CROP_TOP); +static int tvp5150_get_mbus_config(struct v4l2_subdev *sd, + unsigned int pad, + struct v4l2_mbus_config *cfg) +{ + struct tvp5150 *decoder = to_tvp5150(sd); - /* Calculate height based on current standard */ - if (decoder->norm == V4L2_STD_ALL) + cfg->type = decoder->mbus_type; + cfg->bus.parallel.flags = V4L2_MBUS_MASTER + | V4L2_MBUS_PCLK_SAMPLE_RISING + | V4L2_MBUS_FIELD_EVEN_LOW + | V4L2_MBUS_DATA_ACTIVE_HIGH; + + return 0; +} + +/**************************************************************************** + V4L2 subdev pad ops + ****************************************************************************/ +static int tvp5150_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + v4l2_std_id std; + + /* + * Reset selection to maximum on subdev_open() if autodetection is on + * and a standard change is detected. + */ + if (decoder->norm == V4L2_STD_ALL) { std = tvp5150_read_std(sd); - else - std = decoder->norm; + if (std != decoder->detected_norm) { + decoder->detected_norm = std; + tvp5150_set_default(std, &decoder->rect); + } + } - if (std & V4L2_STD_525_60) - hmax = TVP5150_V_MAX_525_60; - else - hmax = TVP5150_V_MAX_OTHERS; - - rect.height = clamp(rect.height, - hmax - TVP5150_MAX_CROP_TOP - rect.top, - hmax - rect.top); - - tvp5150_write(sd, TVP5150_VERT_BLANKING_START, rect.top); - tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, - rect.top + rect.height - hmax); - tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_MSB, - rect.left >> TVP5150_CROP_SHIFT); - tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_LSB, - rect.left | (1 << TVP5150_CROP_SHIFT)); - tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_MSB, - (rect.left + rect.width - TVP5150_MAX_CROP_LEFT) >> - TVP5150_CROP_SHIFT); - tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_LSB, - rect.left + rect.width - TVP5150_MAX_CROP_LEFT); - - decoder->rect = rect; + return 0; +} +static int tvp5150_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad || code->index) + return -EINVAL; + + code->code = TVP5150_MBUS_FMT; return 0; } -static int tvp5150_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +static int tvp5150_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) { - struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd); + struct tvp5150 *decoder = to_tvp5150(sd); + + if (fse->index >= 8 || fse->code != TVP5150_MBUS_FMT) + return -EINVAL; - a->c = decoder->rect; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fse->code = TVP5150_MBUS_FMT; + fse->min_width = decoder->rect.width; + fse->max_width = decoder->rect.width; + fse->min_height = decoder->rect.height / 2; + fse->max_height = decoder->rect.height / 2; return 0; } -static int tvp5150_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +/**************************************************************************** + * Media entity ops + ****************************************************************************/ +#if defined(CONFIG_MEDIA_CONTROLLER) +static int tvp5150_set_link(struct media_pad *connector_pad, + struct media_pad *tvp5150_pad, u32 flags) { - struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd); - v4l2_std_id std; + struct media_link *link; - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + link = media_entity_find_link(connector_pad, tvp5150_pad); + if (!link) return -EINVAL; - a->bounds.left = 0; - a->bounds.top = 0; - a->bounds.width = TVP5150_H_MAX; + link->flags = flags; + link->reverse->flags = link->flags; - /* Calculate height based on current standard */ - if (decoder->norm == V4L2_STD_ALL) - std = tvp5150_read_std(sd); - else - std = decoder->norm; + return 0; +} - if (std & V4L2_STD_525_60) - a->bounds.height = TVP5150_V_MAX_525_60; - else - a->bounds.height = TVP5150_V_MAX_OTHERS; +static int tvp5150_disable_all_input_links(struct tvp5150 *decoder) +{ + struct media_pad *connector_pad; + unsigned int i; + int err; + + for (i = 0; i < TVP5150_NUM_PADS - 1; i++) { + connector_pad = media_pad_remote_pad_first(&decoder->pads[i]); + if (!connector_pad) + continue; + + err = tvp5150_set_link(connector_pad, &decoder->pads[i], 0); + if (err) + return err; + } + + return 0; +} + +static int tvp5150_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, + u32 config); + +static int tvp5150_link_setup(struct media_entity *entity, + const struct media_pad *tvp5150_pad, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct tvp5150 *decoder = to_tvp5150(sd); + struct media_pad *other_tvp5150_pad = + &decoder->pads[tvp5150_pad->index ^ 1]; + struct v4l2_fwnode_connector *v4l2c; + bool is_svideo = false; + unsigned int i; + int err; + + /* + * The TVP5150 state is determined by the enabled sink pad link(s). + * Enabling or disabling the source pad link has no effect. + */ + if (tvp5150_pad->flags & MEDIA_PAD_FL_SOURCE) + return 0; + + /* Check if the svideo connector should be enabled */ + for (i = 0; i < decoder->connectors_num; i++) { + if (remote->entity == &decoder->connectors[i].ent) { + v4l2c = &decoder->connectors[i].base; + is_svideo = v4l2c->type == V4L2_CONN_SVIDEO; + break; + } + } + + dev_dbg_lvl(sd->dev, 1, debug, "link setup '%s':%d->'%s':%d[%d]", + remote->entity->name, remote->index, + tvp5150_pad->entity->name, tvp5150_pad->index, + flags & MEDIA_LNK_FL_ENABLED); + if (is_svideo) + dev_dbg_lvl(sd->dev, 1, debug, + "link setup '%s':%d->'%s':%d[%d]", + remote->entity->name, remote->index, + other_tvp5150_pad->entity->name, + other_tvp5150_pad->index, + flags & MEDIA_LNK_FL_ENABLED); + + /* + * The TVP5150 has an internal mux which allows the following setup: + * + * comp-connector1 --\ + * |---> AIP1A + * / + * svideo-connector -| + * \ + * |---> AIP1B + * comp-connector2 --/ + * + * We can't rely on user space that the current connector gets disabled + * first before enabling the new connector. Disable all active + * connector links to be on the safe side. + */ + err = tvp5150_disable_all_input_links(decoder); + if (err) + return err; + + tvp5150_s_routing(sd, is_svideo ? TVP5150_SVIDEO : tvp5150_pad->index, + flags & MEDIA_LNK_FL_ENABLED ? TVP5150_NORMAL : + TVP5150_BLACK_SCREEN, 0); + + if (flags & MEDIA_LNK_FL_ENABLED) { + struct v4l2_fwnode_connector_analog *v4l2ca; + u32 new_norm; + + /* + * S-Video connector is conneted to both ports AIP1A and AIP1B. + * Both links must be enabled in one-shot regardless which link + * the user requests. + */ + if (is_svideo) { + err = tvp5150_set_link((struct media_pad *)remote, + other_tvp5150_pad, flags); + if (err) + return err; + } + + if (!decoder->connectors_num) + return 0; + + /* Update the current connector */ + decoder->cur_connector = + container_of(remote, struct tvp5150_connector, pad); + + /* + * Do nothing if the new connector supports the same tv-norms as + * the old one. + */ + v4l2ca = &decoder->cur_connector->base.connector.analog; + new_norm = decoder->norm & v4l2ca->sdtv_stds; + if (decoder->norm == new_norm) + return 0; - a->defrect = a->bounds; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; + /* + * Fallback to the new connector tv-norms if we can't find any + * common between the current tv-norm and the new one. + */ + tvp5150_s_std(sd, new_norm ? new_norm : v4l2ca->sdtv_stds); + } return 0; } +static const struct media_entity_operations tvp5150_sd_media_ops = { + .link_setup = tvp5150_link_setup, +}; +#endif /**************************************************************************** I2C Command ****************************************************************************/ +static int __maybe_unused tvp5150_runtime_suspend(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct tvp5150 *decoder = to_tvp5150(sd); + + if (decoder->irq) + /* Disable lock interrupt */ + return regmap_update_bits(decoder->regmap, + TVP5150_INT_ENABLE_REG_A, + TVP5150_INT_A_LOCK, 0); + return 0; +} + +static int __maybe_unused tvp5150_runtime_resume(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct tvp5150 *decoder = to_tvp5150(sd); + + if (decoder->irq) + /* Enable lock interrupt */ + return regmap_update_bits(decoder->regmap, + TVP5150_INT_ENABLE_REG_A, + TVP5150_INT_A_LOCK, + TVP5150_INT_A_LOCK); + return 0; +} + +static int tvp5150_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + unsigned int mask, val = 0; + int ret; + + mask = TVP5150_MISC_CTL_YCBCR_OE | TVP5150_MISC_CTL_SYNC_OE | + TVP5150_MISC_CTL_CLOCK_OE; + + if (enable) { + ret = pm_runtime_resume_and_get(sd->dev); + if (ret < 0) + return ret; + + tvp5150_enable(sd); + + /* Enable outputs if decoder is locked */ + if (decoder->irq) + val = decoder->lock ? decoder->oe : 0; + else + val = decoder->oe; + + v4l2_subdev_notify_event(&decoder->sd, &tvp5150_ev_fmt); + } else { + pm_runtime_put(sd->dev); + } + + regmap_update_bits(decoder->regmap, TVP5150_MISC_CTL, mask, val); + + return 0; +} static int tvp5150_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, u32 config) @@ -966,49 +1479,62 @@ static int tvp5150_s_routing(struct v4l2_subdev *sd, decoder->input = input; decoder->output = output; + + if (output == TVP5150_BLACK_SCREEN) + decoder->enable = false; + else + decoder->enable = true; + tvp5150_selmux(sd); return 0; } static int tvp5150_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) { - /* this is for capturing 36 raw vbi lines - if there's a way to cut off the beginning 2 vbi lines - with the tvp5150 then the vbi line count could be lowered - to 17 lines/field again, although I couldn't find a register - which could do that cropping */ + struct tvp5150 *decoder = to_tvp5150(sd); + + /* + * this is for capturing 36 raw vbi lines + * if there's a way to cut off the beginning 2 vbi lines + * with the tvp5150 then the vbi line count could be lowered + * to 17 lines/field again, although I couldn't find a register + * which could do that cropping + */ + if (fmt->sample_format == V4L2_PIX_FMT_GREY) - tvp5150_write(sd, TVP5150_LUMA_PROC_CTL_1, 0x70); + regmap_write(decoder->regmap, TVP5150_LUMA_PROC_CTL_1, 0x70); if (fmt->count[0] == 18 && fmt->count[1] == 18) { - tvp5150_write(sd, TVP5150_VERT_BLANKING_START, 0x00); - tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, 0x01); + regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_START, + 0x00); + regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_STOP, 0x01); } return 0; } static int tvp5150_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) { + struct tvp5150 *decoder = to_tvp5150(sd); int i; if (svbi->service_set != 0) { for (i = 0; i <= 23; i++) { svbi->service_lines[1][i] = 0; svbi->service_lines[0][i] = - tvp5150_set_vbi(sd, vbi_ram_default, - svbi->service_lines[0][i], 0xf0, i, 3); + tvp5150_set_vbi(sd, svbi->service_lines[0][i], + 0xf0, i, 3); } /* Enables FIFO */ - tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 1); + regmap_write(decoder->regmap, TVP5150_FIFO_OUT_CTRL, 1); } else { /* Disables FIFO*/ - tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 0); + regmap_write(decoder->regmap, TVP5150_FIFO_OUT_CTRL, 0); /* Disable Full Field */ - tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0); + regmap_write(decoder->regmap, TVP5150_FULL_FIELD_ENA, 0); /* Disable Line modes */ for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++) - tvp5150_write(sd, i, 0xff); + regmap_write(decoder->regmap, i, 0xff); } return 0; } @@ -1021,7 +1547,7 @@ static int tvp5150_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_f for (i = 0; i <= 23; i++) { svbi->service_lines[0][i] = - tvp5150_get_vbi(sd, vbi_ram_default, i); + tvp5150_get_vbi(sd, i); mask |= svbi->service_lines[0][i]; } svbi->service_set = mask; @@ -1035,7 +1561,7 @@ static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register * res = tvp5150_read(sd, reg->reg & 0xff); if (res < 0) { - v4l2_err(sd, "%s: failed with error = %d\n", __func__, res); + dev_err(sd->dev, "%s: failed with error = %d\n", __func__, res); return res; } @@ -1046,11 +1572,25 @@ static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register * static int tvp5150_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { - tvp5150_write(sd, reg->reg & 0xff, reg->val & 0xff); - return 0; + struct tvp5150 *decoder = to_tvp5150(sd); + + return regmap_write(decoder->regmap, reg->reg & 0xff, reg->val & 0xff); } #endif +static int tvp5150_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub); + default: + return -EINVAL; + } +} + static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) { int status = tvp5150_read(sd, 0x88); @@ -1059,6 +1599,92 @@ static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) return 0; } +static int tvp5150_registered(struct v4l2_subdev *sd) +{ +#if defined(CONFIG_MEDIA_CONTROLLER) + struct tvp5150 *decoder = to_tvp5150(sd); + unsigned int i; + int ret; + + /* + * Setup connector pads and links. Enable the link to the first + * available connector per default. + */ + for (i = 0; i < decoder->connectors_num; i++) { + struct media_entity *con = &decoder->connectors[i].ent; + struct media_pad *pad = &decoder->connectors[i].pad; + struct v4l2_fwnode_connector *v4l2c = + &decoder->connectors[i].base; + struct v4l2_connector_link *link = + v4l2_connector_first_link(v4l2c); + unsigned int port = link->fwnode_link.remote_port; + unsigned int flags = i ? 0 : MEDIA_LNK_FL_ENABLED; + bool is_svideo = v4l2c->type == V4L2_CONN_SVIDEO; + + pad->flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(con, 1, pad); + if (ret < 0) + goto err; + + ret = media_device_register_entity(sd->v4l2_dev->mdev, con); + if (ret < 0) + goto err; + + ret = media_create_pad_link(con, 0, &sd->entity, port, flags); + if (ret < 0) + goto err; + + if (is_svideo) { + /* + * Check tvp5150_link_setup() comments for more + * information. + */ + link = v4l2_connector_last_link(v4l2c); + port = link->fwnode_link.remote_port; + ret = media_create_pad_link(con, 0, &sd->entity, port, + flags); + if (ret < 0) + goto err; + } + + /* Enable default input. */ + if (flags == MEDIA_LNK_FL_ENABLED) { + decoder->input = + is_svideo ? TVP5150_SVIDEO : + port == 0 ? TVP5150_COMPOSITE0 : + TVP5150_COMPOSITE1; + + tvp5150_selmux(sd); + decoder->cur_connector = &decoder->connectors[i]; + tvp5150_s_std(sd, v4l2c->connector.analog.sdtv_stds); + } + } + + return 0; + +err: + for (i = 0; i < decoder->connectors_num; i++) { + media_device_unregister_entity(&decoder->connectors[i].ent); + media_entity_cleanup(&decoder->connectors[i].ent); + } + return ret; +#endif + + return 0; +} + +static int tvp5150_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + return pm_runtime_resume_and_get(sd->dev); +} + +static int tvp5150_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + pm_runtime_put(sd->dev); + + return 0; +} + /* ----------------------------------------------------------------------- */ static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = { @@ -1067,12 +1693,13 @@ 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, .s_register = tvp5150_s_register, #endif + .subscribe_event = tvp5150_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = { @@ -1080,14 +1707,11 @@ 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, + .g_std = tvp5150_g_std, + .querystd = tvp5150_querystd, + .s_stream = tvp5150_s_stream, .s_routing = tvp5150_s_routing, - .enum_mbus_fmt = tvp5150_enum_mbus_fmt, - .s_mbus_fmt = tvp5150_mbus_fmt, - .try_mbus_fmt = tvp5150_mbus_fmt, - .g_mbus_fmt = tvp5150_mbus_fmt, - .s_crop = tvp5150_s_crop, - .g_crop = tvp5150_g_crop, - .cropcap = tvp5150_cropcap, }; static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = { @@ -1097,75 +1721,468 @@ static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = { .s_raw_fmt = tvp5150_s_raw_fmt, }; +static const struct v4l2_subdev_pad_ops tvp5150_pad_ops = { + .enum_mbus_code = tvp5150_enum_mbus_code, + .enum_frame_size = tvp5150_enum_frame_size, + .set_fmt = tvp5150_fill_fmt, + .get_fmt = tvp5150_fill_fmt, + .get_selection = tvp5150_get_selection, + .set_selection = tvp5150_set_selection, + .get_mbus_config = tvp5150_get_mbus_config, +}; + static const struct v4l2_subdev_ops tvp5150_ops = { .core = &tvp5150_core_ops, .tuner = &tvp5150_tuner_ops, .video = &tvp5150_video_ops, .vbi = &tvp5150_vbi_ops, + .pad = &tvp5150_pad_ops, }; +static const struct v4l2_subdev_internal_ops tvp5150_internal_ops = { + .init_state = tvp5150_init_state, + .registered = tvp5150_registered, + .open = tvp5150_open, + .close = tvp5150_close, +}; /**************************************************************************** I2C Client & Driver ****************************************************************************/ -static int tvp5150_probe(struct i2c_client *c, - const struct i2c_device_id *id) +static const struct regmap_range tvp5150_readable_ranges[] = { + { + .range_min = TVP5150_VD_IN_SRC_SEL_1, + .range_max = TVP5150_AUTOSW_MSK, + }, { + .range_min = TVP5150_COLOR_KIL_THSH_CTL, + .range_max = TVP5150_CONF_SHARED_PIN, + }, { + .range_min = TVP5150_ACT_VD_CROP_ST_MSB, + .range_max = TVP5150_HORIZ_SYNC_START, + }, { + .range_min = TVP5150_VERT_BLANKING_START, + .range_max = TVP5150_INTT_CONFIG_REG_B, + }, { + .range_min = TVP5150_VIDEO_STD, + .range_max = TVP5150_VIDEO_STD, + }, { + .range_min = TVP5150_CB_GAIN_FACT, + .range_max = TVP5150_REV_SELECT, + }, { + .range_min = TVP5150_MSB_DEV_ID, + .range_max = TVP5150_STATUS_REG_5, + }, { + .range_min = TVP5150_CC_DATA_INI, + .range_max = TVP5150_TELETEXT_FIL_ENA, + }, { + .range_min = TVP5150_INT_STATUS_REG_A, + .range_max = TVP5150_FIFO_OUT_CTRL, + }, { + .range_min = TVP5150_FULL_FIELD_ENA, + .range_max = TVP5150_FULL_FIELD_MODE_REG, + }, +}; + +static bool tvp5150_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TVP5150_VERT_LN_COUNT_MSB: + case TVP5150_VERT_LN_COUNT_LSB: + case TVP5150_INT_STATUS_REG_A: + case TVP5150_INT_STATUS_REG_B: + case TVP5150_INT_ACTIVE_REG_B: + case TVP5150_STATUS_REG_1: + case TVP5150_STATUS_REG_2: + case TVP5150_STATUS_REG_3: + case TVP5150_STATUS_REG_4: + case TVP5150_STATUS_REG_5: + /* CC, WSS, VPS, VITC data? */ + case TVP5150_VBI_FIFO_READ_DATA: + case TVP5150_VDP_STATUS_REG: + case TVP5150_FIFO_WORD_COUNT: + return true; + default: + return false; + } +} + +static const struct regmap_access_table tvp5150_readable_table = { + .yes_ranges = tvp5150_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(tvp5150_readable_ranges), +}; + +static const struct regmap_config tvp5150_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xff, + + .cache_type = REGCACHE_MAPLE, + + .rd_table = &tvp5150_readable_table, + .volatile_reg = tvp5150_volatile_reg, +}; + +static int tvp5150_detect_version(struct tvp5150 *core) +{ + struct v4l2_subdev *sd = &core->sd; + struct i2c_client *c = v4l2_get_subdevdata(sd); + u8 regs[4]; + int res; + + /* + * Read consequent registers - TVP5150_MSB_DEV_ID, TVP5150_LSB_DEV_ID, + * TVP5150_ROM_MAJOR_VER, TVP5150_ROM_MINOR_VER + */ + res = regmap_bulk_read(core->regmap, TVP5150_MSB_DEV_ID, regs, 4); + if (res < 0) { + dev_err(&c->dev, "reading ID registers failed: %d\n", res); + return res; + } + + core->dev_id = (regs[0] << 8) | regs[1]; + core->rom_ver = (regs[2] << 8) | regs[3]; + + dev_info(sd->dev, "tvp%04x (%u.%u) chip found @ 0x%02x (%s)\n", + core->dev_id, regs[2], regs[3], c->addr << 1, + c->adapter->name); + + if (core->dev_id == 0x5150 && core->rom_ver == 0x0321) { + dev_info(sd->dev, "tvp5150a detected.\n"); + } else if (core->dev_id == 0x5150 && core->rom_ver == 0x0400) { + dev_info(sd->dev, "tvp5150am1 detected.\n"); + + /* ITU-T BT.656.4 timing */ + regmap_write(core->regmap, TVP5150_REV_SELECT, 0); + } else if (core->dev_id == 0x5151 && core->rom_ver == 0x0100) { + dev_info(sd->dev, "tvp5151 detected.\n"); + } else { + dev_info(sd->dev, "*** unknown tvp%04x chip detected.\n", + core->dev_id); + } + + return 0; +} + +static int tvp5150_init(struct i2c_client *c) +{ + struct gpio_desc *pdn_gpio; + struct gpio_desc *reset_gpio; + + pdn_gpio = devm_gpiod_get_optional(&c->dev, "pdn", GPIOD_OUT_HIGH); + if (IS_ERR(pdn_gpio)) + return PTR_ERR(pdn_gpio); + + if (pdn_gpio) { + gpiod_set_value_cansleep(pdn_gpio, 0); + /* Delay time between power supplies active and reset */ + msleep(20); + } + + reset_gpio = devm_gpiod_get_optional(&c->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(reset_gpio)) + return PTR_ERR(reset_gpio); + + if (reset_gpio) { + /* RESETB pulse duration */ + ndelay(500); + gpiod_set_value_cansleep(reset_gpio, 0); + /* Delay time between end of reset to I2C active */ + usleep_range(200, 250); + } + + return 0; +} + +#if defined(CONFIG_MEDIA_CONTROLLER) +static int tvp5150_mc_init(struct tvp5150 *decoder) +{ + struct v4l2_subdev *sd = &decoder->sd; + unsigned int i; + + sd->entity.ops = &tvp5150_sd_media_ops; + sd->entity.function = MEDIA_ENT_F_ATV_DECODER; + + for (i = 0; i < TVP5150_NUM_PADS - 1; i++) { + decoder->pads[i].flags = MEDIA_PAD_FL_SINK; + decoder->pads[i].sig_type = PAD_SIGNAL_ANALOG; + } + + decoder->pads[i].flags = MEDIA_PAD_FL_SOURCE; + decoder->pads[i].sig_type = PAD_SIGNAL_DV; + + return media_entity_pads_init(&sd->entity, TVP5150_NUM_PADS, + decoder->pads); +} + +#else /* !defined(CONFIG_MEDIA_CONTROLLER) */ + +static inline int tvp5150_mc_init(struct tvp5150 *decoder) +{ + return 0; +} +#endif /* defined(CONFIG_MEDIA_CONTROLLER) */ + +static int tvp5150_validate_connectors(struct tvp5150 *decoder) +{ + struct device *dev = decoder->sd.dev; + struct tvp5150_connector *tvpc; + struct v4l2_fwnode_connector *v4l2c; + unsigned int i; + + if (!decoder->connectors_num) { + dev_err(dev, "No valid connector found\n"); + return -ENODEV; + } + + for (i = 0; i < decoder->connectors_num; i++) { + struct v4l2_connector_link *link0 = NULL; + struct v4l2_connector_link *link1; + + tvpc = &decoder->connectors[i]; + v4l2c = &tvpc->base; + + if (v4l2c->type == V4L2_CONN_COMPOSITE) { + if (v4l2c->nr_of_links != 1) { + dev_err(dev, "Composite: connector needs 1 link\n"); + return -EINVAL; + } + link0 = v4l2_connector_first_link(v4l2c); + if (!link0) { + dev_err(dev, "Composite: invalid first link\n"); + return -EINVAL; + } + if (link0->fwnode_link.remote_id == 1) { + dev_err(dev, "Composite: invalid endpoint id\n"); + return -EINVAL; + } + } + + if (v4l2c->type == V4L2_CONN_SVIDEO) { + if (v4l2c->nr_of_links != 2) { + dev_err(dev, "SVideo: connector needs 2 links\n"); + return -EINVAL; + } + link0 = v4l2_connector_first_link(v4l2c); + if (!link0) { + dev_err(dev, "SVideo: invalid first link\n"); + return -EINVAL; + } + link1 = v4l2_connector_last_link(v4l2c); + if (link0->fwnode_link.remote_port == + link1->fwnode_link.remote_port) { + dev_err(dev, "SVideo: invalid link setup\n"); + return -EINVAL; + } + } + + if (!(v4l2c->connector.analog.sdtv_stds & TVP5150_STD_MASK)) { + dev_err(dev, "Unsupported tv-norm on connector %s\n", + v4l2c->name); + return -EINVAL; + } + } + + return 0; +} + +static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np) +{ + struct device *dev = decoder->sd.dev; + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_UNKNOWN + }; + struct device_node *ep_np; + struct tvp5150_connector *tvpc; + struct v4l2_fwnode_connector *v4l2c; + unsigned int flags, ep_num; + unsigned int i; + int ret; + + /* At least 1 output and 1 input */ + ep_num = of_graph_get_endpoint_count(np); + if (ep_num < 2 || ep_num > 5) { + dev_err(dev, "At least 1 input and 1 output must be connected to the device.\n"); + return -EINVAL; + } + + /* Layout if all connectors are used: + * + * tvp-5150 port@0 (AIP1A) + * endpoint@0 -----------> Comp0-Con port + * endpoint@1 --------+--> Svideo-Con port + * tvp-5150 port@1 (AIP1B) | + * endpoint@1 --------+ + * endpoint@0 -----------> Comp1-Con port + * tvp-5150 port@2 + * endpoint (video bitstream output at YOUT[0-7] parallel bus) + */ + for_each_endpoint_of_node(np, ep_np) { + struct fwnode_handle *ep_fwnode = of_fwnode_handle(ep_np); + unsigned int next_connector = decoder->connectors_num; + struct of_endpoint ep; + + of_graph_parse_endpoint(ep_np, &ep); + if (ep.port > 1 || ep.id > 1) { + dev_dbg(dev, "Ignore connector on port@%u/ep@%u\n", + ep.port, ep.id); + continue; + } + + tvpc = &decoder->connectors[next_connector]; + v4l2c = &tvpc->base; + + if (ep.port == 0 || (ep.port == 1 && ep.id == 0)) { + ret = v4l2_fwnode_connector_parse(ep_fwnode, v4l2c); + if (ret) + goto err_put; + ret = v4l2_fwnode_connector_add_link(ep_fwnode, v4l2c); + if (ret) + goto err_put; + decoder->connectors_num++; + } else { + /* Adding the 2nd svideo link */ + for (i = 0; i < TVP5150_MAX_CONNECTORS; i++) { + tvpc = &decoder->connectors[i]; + v4l2c = &tvpc->base; + if (v4l2c->type == V4L2_CONN_SVIDEO) + break; + } + + ret = v4l2_fwnode_connector_add_link(ep_fwnode, v4l2c); + if (ret) + goto err_put; + } + } + + ret = tvp5150_validate_connectors(decoder); + if (ret) + goto err_free; + + for (i = 0; i < decoder->connectors_num; i++) { + tvpc = &decoder->connectors[i]; + v4l2c = &tvpc->base; + tvpc->ent.flags = MEDIA_ENT_FL_CONNECTOR; + tvpc->ent.function = v4l2c->type == V4L2_CONN_SVIDEO ? + MEDIA_ENT_F_CONN_SVIDEO : MEDIA_ENT_F_CONN_COMPOSITE; + tvpc->ent.name = devm_kasprintf(dev, GFP_KERNEL, "%s %s", + v4l2c->name, v4l2c->label ? + v4l2c->label : ""); + if (!tvpc->ent.name) { + ret = -ENOMEM; + goto err_free; + } + } + + ep_np = of_graph_get_endpoint_by_regs(np, TVP5150_PAD_VID_OUT, 0); + if (!ep_np) { + ret = -EINVAL; + dev_err(dev, "Error no output endpoint available\n"); + goto err_free; + } + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_np), &bus_cfg); + of_node_put(ep_np); + if (ret) + goto err_free; + + flags = bus_cfg.bus.parallel.flags; + if (bus_cfg.bus_type == V4L2_MBUS_PARALLEL && + !(flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH && + flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH && + flags & V4L2_MBUS_FIELD_EVEN_LOW)) { + ret = -EINVAL; + goto err_free; + } + + decoder->mbus_type = bus_cfg.bus_type; + + return 0; + +err_put: + of_node_put(ep_np); +err_free: + for (i = 0; i < TVP5150_MAX_CONNECTORS; i++) + v4l2_fwnode_connector_free(&decoder->connectors[i].base); + + return ret; +} + +static const char * const tvp5150_test_patterns[2] = { + "Disabled", + "Black screen" +}; + +static int tvp5150_probe(struct i2c_client *c) { struct tvp5150 *core; struct v4l2_subdev *sd; - int tvp5150_id[4]; - int i, res; + struct device_node *np = c->dev.of_node; + struct regmap *map; + unsigned int i; + int res; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(c->adapter, I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) return -EIO; + res = tvp5150_init(c); + if (res) + return res; + core = devm_kzalloc(&c->dev, sizeof(*core), GFP_KERNEL); if (!core) return -ENOMEM; + + map = devm_regmap_init_i2c(c, &tvp5150_config); + if (IS_ERR(map)) + return PTR_ERR(map); + + core->regmap = map; sd = &core->sd; v4l2_i2c_subdev_init(sd, c, &tvp5150_ops); + sd->internal_ops = &tvp5150_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; - /* - * Read consequent registers - TVP5150_MSB_DEV_ID, TVP5150_LSB_DEV_ID, - * TVP5150_ROM_MAJOR_VER, TVP5150_ROM_MINOR_VER - */ - for (i = 0; i < 4; i++) { - res = tvp5150_read(sd, TVP5150_MSB_DEV_ID + i); - if (res < 0) + if (IS_ENABLED(CONFIG_OF) && np) { + res = tvp5150_parse_dt(core, np); + if (res) { + dev_err(sd->dev, "DT parsing error: %d\n", res); return res; - tvp5150_id[i] = res; + } + } else { + /* Default to BT.656 embedded sync */ + core->mbus_type = V4L2_MBUS_BT656; } - v4l_info(c, "chip found @ 0x%02x (%s)\n", - c->addr << 1, c->adapter->name); + res = tvp5150_mc_init(core); + if (res) + return res; - if (tvp5150_id[2] == 4 && tvp5150_id[3] == 0) { /* Is TVP5150AM1 */ - v4l2_info(sd, "tvp%02x%02xam1 detected.\n", - tvp5150_id[0], tvp5150_id[1]); + res = tvp5150_detect_version(core); + if (res < 0) + return res; - /* ITU-T BT.656.4 timing */ - tvp5150_write(sd, TVP5150_REV_SELECT, 0); - } else { - /* Is TVP5150A */ - if (tvp5150_id[2] == 3 || tvp5150_id[3] == 0x21) { - v4l2_info(sd, "tvp%02x%02xa detected.\n", - tvp5150_id[2], tvp5150_id[3]); - } else { - v4l2_info(sd, "*** unknown tvp%02x%02x chip detected.\n", - tvp5150_id[2], tvp5150_id[3]); - v4l2_info(sd, "*** Rom ver is %d.%d\n", - tvp5150_id[2], tvp5150_id[3]); - } + /* + * Iterate over all available connectors in case they are supported and + * successfully parsed. Fallback to default autodetect in case they + * aren't supported. + */ + for (i = 0; i < core->connectors_num; i++) { + struct v4l2_fwnode_connector *v4l2c; + + v4l2c = &core->connectors[i].base; + core->norm |= v4l2c->connector.analog.sdtv_stds; } - core->norm = V4L2_STD_ALL; /* Default is autodetect */ + if (!core->connectors_num) + core->norm = V4L2_STD_ALL; + + core->detected_norm = V4L2_STD_UNKNOWN; core->input = TVP5150_COMPOSITE1; - core->enable = 1; + core->enable = true; - v4l2_ctrl_handler_init(&core->hdl, 4); + v4l2_ctrl_handler_init(&core->hdl, 5); v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, @@ -1174,54 +2191,98 @@ static int tvp5150_probe(struct i2c_client *c, V4L2_CID_SATURATION, 0, 255, 1, 128); v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, V4L2_CID_HUE, -128, 127, 1, 0); + v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, + V4L2_CID_PIXEL_RATE, 27000000, + 27000000, 1, 27000000); + v4l2_ctrl_new_std_menu_items(&core->hdl, &tvp5150_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(tvp5150_test_patterns) - 1, + 0, 0, tvp5150_test_patterns); sd->ctrl_handler = &core->hdl; if (core->hdl.error) { res = core->hdl.error; - v4l2_ctrl_handler_free(&core->hdl); - return res; + goto err; } - v4l2_ctrl_handler_setup(&core->hdl); - /* Default is no cropping */ - core->rect.top = 0; - if (tvp5150_read_std(sd) & V4L2_STD_525_60) - core->rect.height = TVP5150_V_MAX_525_60; - else - core->rect.height = TVP5150_V_MAX_OTHERS; - core->rect.left = 0; - core->rect.width = TVP5150_H_MAX; + tvp5150_set_default(tvp5150_read_std(sd), &core->rect); + + core->irq = c->irq; + tvp5150_reset(sd, 0); /* Calls v4l2_ctrl_handler_setup() */ + if (c->irq) { + res = devm_request_threaded_irq(&c->dev, c->irq, NULL, + tvp5150_isr, IRQF_TRIGGER_HIGH | + IRQF_ONESHOT, "tvp5150", core); + if (res) + goto err; + } + + res = v4l2_async_register_subdev(sd); + if (res < 0) + goto err; if (debug > 1) tvp5150_log_status(sd); + + pm_runtime_set_active(&c->dev); + pm_runtime_enable(&c->dev); + pm_runtime_idle(&c->dev); + return 0; + +err: + v4l2_ctrl_handler_free(&core->hdl); + return res; } -static int tvp5150_remove(struct i2c_client *c) +static void tvp5150_remove(struct i2c_client *c) { struct v4l2_subdev *sd = i2c_get_clientdata(c); struct tvp5150 *decoder = to_tvp5150(sd); + unsigned int i; - v4l2_dbg(1, debug, sd, + dev_dbg_lvl(sd->dev, 1, debug, "tvp5150.c: removing tvp5150 adapter on address 0x%x\n", c->addr << 1); - v4l2_device_unregister_subdev(sd); + for (i = 0; i < decoder->connectors_num; i++) + v4l2_fwnode_connector_free(&decoder->connectors[i].base); + for (i = 0; i < decoder->connectors_num; i++) { + media_device_unregister_entity(&decoder->connectors[i].ent); + media_entity_cleanup(&decoder->connectors[i].ent); + } + v4l2_async_unregister_subdev(sd); v4l2_ctrl_handler_free(&decoder->hdl); - return 0; + pm_runtime_disable(&c->dev); + pm_runtime_set_suspended(&c->dev); } /* ----------------------------------------------------------------------- */ +static const struct dev_pm_ops tvp5150_pm_ops = { + SET_RUNTIME_PM_OPS(tvp5150_runtime_suspend, + tvp5150_runtime_resume, + NULL) +}; + static const struct i2c_device_id tvp5150_id[] = { - { "tvp5150", 0 }, + { "tvp5150" }, { } }; MODULE_DEVICE_TABLE(i2c, tvp5150_id); +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id tvp5150_of_match[] = { + { .compatible = "ti,tvp5150", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, tvp5150_of_match); +#endif + static struct i2c_driver tvp5150_driver = { .driver = { - .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tvp5150_of_match), .name = "tvp5150", + .pm = &tvp5150_pm_ops, }, .probe = tvp5150_probe, .remove = tvp5150_remove, |
