diff options
Diffstat (limited to 'drivers/media/i2c/ds90ub960.c')
-rw-r--r-- | drivers/media/i2c/ds90ub960.c | 190 |
1 files changed, 127 insertions, 63 deletions
diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c index 33f362a00875..5dde8452739b 100644 --- a/drivers/media/i2c/ds90ub960.c +++ b/drivers/media/i2c/ds90ub960.c @@ -43,6 +43,7 @@ #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> +#include <linux/units.h> #include <linux/workqueue.h> #include <media/i2c/ds90ub9xx.h> @@ -51,7 +52,16 @@ #include <media/v4l2-fwnode.h> #include <media/v4l2-subdev.h> -#define MHZ(v) ((u32)((v) * 1000000U)) +#define MHZ(v) ((u32)((v) * HZ_PER_MHZ)) + +/* + * If this is defined, the i2c addresses from UB960_DEBUG_I2C_RX_ID to + * UB960_DEBUG_I2C_RX_ID + 3 can be used to access the paged RX port registers + * directly. + * + * Only for debug purposes. + */ +/* #define UB960_DEBUG_I2C_RX_ID 0x40 */ #define UB960_POLL_TIME_MS 500 @@ -349,12 +359,13 @@ #define UB960_SR_FPD3_RX_ID(n) (0xf0 + (n)) #define UB960_SR_FPD3_RX_ID_LEN 6 -#define UB960_SR_I2C_RX_ID(n) (0xf8 + (n)) /* < UB960_FPD_RX_NPORTS */ +#define UB960_SR_I2C_RX_ID(n) (0xf8 + (n)) + +#define UB9702_SR_REFCLK_FREQ 0x3d /* Indirect register blocks */ #define UB960_IND_TARGET_PAT_GEN 0x00 #define UB960_IND_TARGET_RX_ANA(n) (0x01 + (n)) -#define UB960_IND_TARGET_CSI_CSIPLL_REG_1 0x92 /* UB9702 */ #define UB960_IND_TARGET_CSI_ANA 0x07 /* UB960_IR_PGEN_*: Indirect Registers for Test Pattern Generator */ @@ -568,11 +579,23 @@ struct ub960_format_info { }; static const struct ub960_format_info ub960_formats[] = { + { .code = MEDIA_BUS_FMT_RGB888_1X24, .bpp = 24, .datatype = MIPI_CSI2_DT_RGB888, }, + { .code = MEDIA_BUS_FMT_YUYV8_1X16, .bpp = 16, .datatype = MIPI_CSI2_DT_YUV422_8B, }, { .code = MEDIA_BUS_FMT_UYVY8_1X16, .bpp = 16, .datatype = MIPI_CSI2_DT_YUV422_8B, }, { .code = MEDIA_BUS_FMT_VYUY8_1X16, .bpp = 16, .datatype = MIPI_CSI2_DT_YUV422_8B, }, { .code = MEDIA_BUS_FMT_YVYU8_1X16, .bpp = 16, .datatype = MIPI_CSI2_DT_YUV422_8B, }, + { .code = MEDIA_BUS_FMT_SBGGR8_1X8, .bpp = 8, .datatype = MIPI_CSI2_DT_RAW8, }, + { .code = MEDIA_BUS_FMT_SGBRG8_1X8, .bpp = 8, .datatype = MIPI_CSI2_DT_RAW8, }, + { .code = MEDIA_BUS_FMT_SGRBG8_1X8, .bpp = 8, .datatype = MIPI_CSI2_DT_RAW8, }, + { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .bpp = 8, .datatype = MIPI_CSI2_DT_RAW8, }, + + { .code = MEDIA_BUS_FMT_SBGGR10_1X10, .bpp = 10, .datatype = MIPI_CSI2_DT_RAW10, }, + { .code = MEDIA_BUS_FMT_SGBRG10_1X10, .bpp = 10, .datatype = MIPI_CSI2_DT_RAW10, }, + { .code = MEDIA_BUS_FMT_SGRBG10_1X10, .bpp = 10, .datatype = MIPI_CSI2_DT_RAW10, }, + { .code = MEDIA_BUS_FMT_SRGGB10_1X10, .bpp = 10, .datatype = MIPI_CSI2_DT_RAW10, }, + { .code = MEDIA_BUS_FMT_SBGGR12_1X12, .bpp = 12, .datatype = MIPI_CSI2_DT_RAW12, }, { .code = MEDIA_BUS_FMT_SGBRG12_1X12, .bpp = 12, .datatype = MIPI_CSI2_DT_RAW12, }, { .code = MEDIA_BUS_FMT_SGRBG12_1X12, .bpp = 12, .datatype = MIPI_CSI2_DT_RAW12, }, @@ -1552,7 +1575,12 @@ static int ub960_rxport_wait_locks(struct ub960_data *priv, if (missing == 0) break; - msleep(50); + /* + * The sleep time of 10 ms was found by testing to give a lock + * with a few iterations. It can be decreased if on some setups + * the lock can be achieved much faster. + */ + fsleep(10 * USEC_PER_MSEC); } if (lock_mask) @@ -1574,16 +1602,24 @@ static int ub960_rxport_wait_locks(struct ub960_data *priv, ub960_rxport_read16(priv, nport, UB960_RR_RX_FREQ_HIGH, &v); - ret = ub960_rxport_get_strobe_pos(priv, nport, &strobe_pos); - if (ret) - return ret; + if (priv->hw_data->is_ub9702) { + dev_dbg(dev, "\trx%u: locked, freq %llu Hz\n", + nport, ((u64)v * HZ_PER_MHZ) >> 8); + } else { + ret = ub960_rxport_get_strobe_pos(priv, nport, + &strobe_pos); + if (ret) + return ret; - ret = ub960_rxport_get_eq_level(priv, nport, &eq_level); - if (ret) - return ret; + ret = ub960_rxport_get_eq_level(priv, nport, &eq_level); + if (ret) + return ret; - dev_dbg(dev, "\trx%u: locked, SP: %d, EQ: %u, freq %llu Hz\n", - nport, strobe_pos, eq_level, (v * 1000000ULL) >> 8); + dev_dbg(dev, + "\trx%u: locked, SP: %d, EQ: %u, freq %llu Hz\n", + nport, strobe_pos, eq_level, + ((u64)v * HZ_PER_MHZ) >> 8); + } } return 0; @@ -2412,7 +2448,6 @@ static int ub960_configure_ports_for_streaming(struct ub960_data *priv, } rx_data[UB960_MAX_RX_NPORTS] = {}; u8 vc_map[UB960_MAX_RX_NPORTS] = {}; struct v4l2_subdev_route *route; - unsigned int nport; int ret; ret = ub960_validate_stream_vcs(priv); @@ -2482,7 +2517,8 @@ static int ub960_configure_ports_for_streaming(struct ub960_data *priv, */ fwd_ctl = GENMASK(7, 4); - for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + for (unsigned int nport = 0; nport < priv->hw_data->num_rxports; + nport++) { struct ub960_rxport *rxport = priv->rxports[nport]; u8 vc = vc_map[nport]; @@ -2522,7 +2558,7 @@ static int ub960_configure_ports_for_streaming(struct ub960_data *priv, for (i = 0; i < 8; i++) ub960_rxport_write(priv, nport, UB960_RR_VC_ID_MAP(i), - nport); + (nport << 4) | nport); } break; @@ -2939,20 +2975,78 @@ static const struct v4l2_subdev_pad_ops ub960_pad_ops = { .set_fmt = ub960_set_fmt, }; +static void ub960_log_status_ub960_sp_eq(struct ub960_data *priv, + unsigned int nport) +{ + struct device *dev = &priv->client->dev; + u8 eq_level; + s8 strobe_pos; + int ret; + u8 v; + + /* Strobe */ + + ret = ub960_read(priv, UB960_XR_AEQ_CTL1, &v); + if (ret) + return; + + dev_info(dev, "\t%s strobe\n", + (v & UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN) ? "Adaptive" : + "Manual"); + + if (v & UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN) { + ret = ub960_read(priv, UB960_XR_SFILTER_CFG, &v); + if (ret) + return; + + dev_info(dev, "\tStrobe range [%d, %d]\n", + ((v >> UB960_XR_SFILTER_CFG_SFILTER_MIN_SHIFT) & 0xf) - 7, + ((v >> UB960_XR_SFILTER_CFG_SFILTER_MAX_SHIFT) & 0xf) - 7); + } + + ret = ub960_rxport_get_strobe_pos(priv, nport, &strobe_pos); + if (ret) + return; + + dev_info(dev, "\tStrobe pos %d\n", strobe_pos); + + /* EQ */ + + ret = ub960_rxport_read(priv, nport, UB960_RR_AEQ_BYPASS, &v); + if (ret) + return; + + dev_info(dev, "\t%s EQ\n", + (v & UB960_RR_AEQ_BYPASS_ENABLE) ? "Manual" : + "Adaptive"); + + if (!(v & UB960_RR_AEQ_BYPASS_ENABLE)) { + ret = ub960_rxport_read(priv, nport, UB960_RR_AEQ_MIN_MAX, &v); + if (ret) + return; + + dev_info(dev, "\tEQ range [%u, %u]\n", + (v >> UB960_RR_AEQ_MIN_MAX_AEQ_FLOOR_SHIFT) & 0xf, + (v >> UB960_RR_AEQ_MIN_MAX_AEQ_MAX_SHIFT) & 0xf); + } + + if (ub960_rxport_get_eq_level(priv, nport, &eq_level) == 0) + dev_info(dev, "\tEQ level %u\n", eq_level); +} + static int ub960_log_status(struct v4l2_subdev *sd) { struct ub960_data *priv = sd_to_ub960(sd); struct device *dev = &priv->client->dev; struct v4l2_subdev_state *state; unsigned int nport; - unsigned int i; u16 v16 = 0; u8 v = 0; u8 id[UB960_SR_FPD3_RX_ID_LEN]; state = v4l2_subdev_lock_and_get_active_state(sd); - for (i = 0; i < sizeof(id); i++) + for (unsigned int i = 0; i < sizeof(id); i++) ub960_read(priv, UB960_SR_FPD3_RX_ID(i), &id[i]); dev_info(dev, "ID '%.*s'\n", (int)sizeof(id), id); @@ -2986,9 +3080,6 @@ static int ub960_log_status(struct v4l2_subdev *sd) for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { struct ub960_rxport *rxport = priv->rxports[nport]; - u8 eq_level; - s8 strobe_pos; - unsigned int i; dev_info(dev, "RX %u\n", nport); @@ -3009,7 +3100,7 @@ static int ub960_log_status(struct v4l2_subdev *sd) dev_info(dev, "\trx_port_sts2 %#02x\n", v); ub960_rxport_read16(priv, nport, UB960_RR_RX_FREQ_HIGH, &v16); - dev_info(dev, "\tlink freq %llu Hz\n", (v16 * 1000000ULL) >> 8); + dev_info(dev, "\tlink freq %llu Hz\n", ((u64)v16 * HZ_PER_MHZ) >> 8); ub960_rxport_read16(priv, nport, UB960_RR_RX_PAR_ERR_HI, &v16); dev_info(dev, "\tparity errors %u\n", v16); @@ -3023,47 +3114,11 @@ static int ub960_log_status(struct v4l2_subdev *sd) ub960_rxport_read(priv, nport, UB960_RR_CSI_ERR_COUNTER, &v); dev_info(dev, "\tcsi_err_counter %u\n", v); - /* Strobe */ - - ub960_read(priv, UB960_XR_AEQ_CTL1, &v); - - dev_info(dev, "\t%s strobe\n", - (v & UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN) ? "Adaptive" : - "Manual"); - - if (v & UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN) { - ub960_read(priv, UB960_XR_SFILTER_CFG, &v); - - dev_info(dev, "\tStrobe range [%d, %d]\n", - ((v >> UB960_XR_SFILTER_CFG_SFILTER_MIN_SHIFT) & 0xf) - 7, - ((v >> UB960_XR_SFILTER_CFG_SFILTER_MAX_SHIFT) & 0xf) - 7); - } - - ub960_rxport_get_strobe_pos(priv, nport, &strobe_pos); - - dev_info(dev, "\tStrobe pos %d\n", strobe_pos); - - /* EQ */ - - ub960_rxport_read(priv, nport, UB960_RR_AEQ_BYPASS, &v); - - dev_info(dev, "\t%s EQ\n", - (v & UB960_RR_AEQ_BYPASS_ENABLE) ? "Manual" : - "Adaptive"); - - if (!(v & UB960_RR_AEQ_BYPASS_ENABLE)) { - ub960_rxport_read(priv, nport, UB960_RR_AEQ_MIN_MAX, &v); - - dev_info(dev, "\tEQ range [%u, %u]\n", - (v >> UB960_RR_AEQ_MIN_MAX_AEQ_FLOOR_SHIFT) & 0xf, - (v >> UB960_RR_AEQ_MIN_MAX_AEQ_MAX_SHIFT) & 0xf); - } - - if (ub960_rxport_get_eq_level(priv, nport, &eq_level) == 0) - dev_info(dev, "\tEQ level %u\n", eq_level); + if (!priv->hw_data->is_ub9702) + ub960_log_status_ub960_sp_eq(priv, nport); /* GPIOs */ - for (i = 0; i < UB960_NUM_BC_GPIOS; i++) { + for (unsigned int i = 0; i < UB960_NUM_BC_GPIOS; i++) { u8 ctl_reg; u8 ctl_shift; @@ -3834,13 +3889,16 @@ static int ub960_enable_core_hw(struct ub960_data *priv) if (ret) goto err_pd_gpio; - ret = ub960_read(priv, UB960_XR_REFCLK_FREQ, &refclk_freq); + if (priv->hw_data->is_ub9702) + ret = ub960_read(priv, UB9702_SR_REFCLK_FREQ, &refclk_freq); + else + ret = ub960_read(priv, UB960_XR_REFCLK_FREQ, &refclk_freq); if (ret) goto err_pd_gpio; dev_dbg(dev, "refclk valid %u freq %u MHz (clk fw freq %lu MHz)\n", !!(dev_sts & BIT(4)), refclk_freq, - clk_get_rate(priv->refclk) / 1000000); + clk_get_rate(priv->refclk) / HZ_PER_MHZ); /* Disable all RX ports by default */ ret = ub960_write(priv, UB960_SR_RX_PORT_CTL, 0); @@ -3974,6 +4032,12 @@ static int ub960_probe(struct i2c_client *client) schedule_delayed_work(&priv->poll_work, msecs_to_jiffies(UB960_POLL_TIME_MS)); +#ifdef UB960_DEBUG_I2C_RX_ID + for (unsigned int i = 0; i < priv->hw_data->num_rxports; i++) + ub960_write(priv, UB960_SR_I2C_RX_ID(i), + (UB960_DEBUG_I2C_RX_ID + i) << 1); +#endif + return 0; err_free_sers: @@ -4052,4 +4116,4 @@ MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Texas Instruments FPD-Link III/IV Deserializers Driver"); MODULE_AUTHOR("Luca Ceresoli <luca@lucaceresoli.net>"); MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>"); -MODULE_IMPORT_NS(I2C_ATR); +MODULE_IMPORT_NS("I2C_ATR"); |