diff options
Diffstat (limited to 'drivers/media/i2c/smiapp')
-rw-r--r-- | drivers/media/i2c/smiapp/smiapp-core.c | 259 | ||||
-rw-r--r-- | drivers/media/i2c/smiapp/smiapp.h | 8 |
2 files changed, 176 insertions, 91 deletions
diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index b10aaeda2bb4..0df5070e73c7 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -205,12 +205,12 @@ static int smiapp_pll_configure(struct smiapp_sensor *sensor) int rval; rval = smiapp_write( - sensor, SMIAPP_REG_U16_VT_PIX_CLK_DIV, pll->vt_pix_clk_div); + sensor, SMIAPP_REG_U16_VT_PIX_CLK_DIV, pll->vt.pix_clk_div); if (rval < 0) return rval; rval = smiapp_write( - sensor, SMIAPP_REG_U16_VT_SYS_CLK_DIV, pll->vt_sys_clk_div); + sensor, SMIAPP_REG_U16_VT_SYS_CLK_DIV, pll->vt.sys_clk_div); if (rval < 0) return rval; @@ -227,20 +227,21 @@ static int smiapp_pll_configure(struct smiapp_sensor *sensor) /* Lane op clock ratio does not apply here. */ rval = smiapp_write( sensor, SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS, - DIV_ROUND_UP(pll->op_sys_clk_freq_hz, 1000000 / 256 / 256)); + DIV_ROUND_UP(pll->op.sys_clk_freq_hz, 1000000 / 256 / 256)); if (rval < 0 || sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) return rval; rval = smiapp_write( - sensor, SMIAPP_REG_U16_OP_PIX_CLK_DIV, pll->op_pix_clk_div); + sensor, SMIAPP_REG_U16_OP_PIX_CLK_DIV, pll->op.pix_clk_div); if (rval < 0) return rval; return smiapp_write( - sensor, SMIAPP_REG_U16_OP_SYS_CLK_DIV, pll->op_sys_clk_div); + sensor, SMIAPP_REG_U16_OP_SYS_CLK_DIV, pll->op.sys_clk_div); } -static int smiapp_pll_update(struct smiapp_sensor *sensor) +static int smiapp_pll_try(struct smiapp_sensor *sensor, + struct smiapp_pll *pll) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); struct smiapp_pll_limits lim = { @@ -274,19 +275,15 @@ static int smiapp_pll_update(struct smiapp_sensor *sensor) .min_line_length_pck_bin = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN], .min_line_length_pck = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK], }; + + return smiapp_pll_calculate(&client->dev, &lim, pll); +} + +static int smiapp_pll_update(struct smiapp_sensor *sensor) +{ struct smiapp_pll *pll = &sensor->pll; int rval; - if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) { - /* - * Fill in operational clock divisors limits from the - * video timing ones. On profile 0 sensors the - * requirements regarding them are essentially the - * same as on VT ones. - */ - lim.op = lim.vt; - } - pll->binning_horizontal = sensor->binning_horizontal; pll->binning_vertical = sensor->binning_vertical; pll->link_freq = @@ -294,12 +291,12 @@ static int smiapp_pll_update(struct smiapp_sensor *sensor) pll->scale_m = sensor->scale_m; pll->bits_per_pixel = sensor->csi_format->compressed; - rval = smiapp_pll_calculate(&client->dev, &lim, pll); + rval = smiapp_pll_try(sensor, pll); if (rval < 0) return rval; __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate_parray, - pll->vt_pix_clk_freq_hz); + pll->pixel_rate_pixel_array); __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate_csi, pll->pixel_rate_csi); return 0; @@ -333,22 +330,22 @@ static void __smiapp_update_exposure_limits(struct smiapp_sensor *sensor) * orders must be defined. */ static const struct smiapp_csi_data_format smiapp_csi_data_formats[] = { - { V4L2_MBUS_FMT_SGRBG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GRBG, }, - { V4L2_MBUS_FMT_SRGGB12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_RGGB, }, - { V4L2_MBUS_FMT_SBGGR12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_BGGR, }, - { V4L2_MBUS_FMT_SGBRG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GBRG, }, - { V4L2_MBUS_FMT_SGRBG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GRBG, }, - { V4L2_MBUS_FMT_SRGGB10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_RGGB, }, - { V4L2_MBUS_FMT_SBGGR10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_BGGR, }, - { V4L2_MBUS_FMT_SGBRG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GBRG, }, - { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GRBG, }, - { V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_RGGB, }, - { V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_BGGR, }, - { V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GBRG, }, - { V4L2_MBUS_FMT_SGRBG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GRBG, }, - { V4L2_MBUS_FMT_SRGGB8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_RGGB, }, - { V4L2_MBUS_FMT_SBGGR8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_BGGR, }, - { V4L2_MBUS_FMT_SGBRG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GBRG, }, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GRBG, }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_RGGB, }, + { MEDIA_BUS_FMT_SBGGR12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_BGGR, }, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GBRG, }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GRBG, }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_RGGB, }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_BGGR, }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GBRG, }, + { MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GRBG, }, + { MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_RGGB, }, + { MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_BGGR, }, + { MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GBRG, }, + { MEDIA_BUS_FMT_SGRBG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GRBG, }, + { MEDIA_BUS_FMT_SRGGB8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_RGGB, }, + { MEDIA_BUS_FMT_SBGGR8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_BGGR, }, + { MEDIA_BUS_FMT_SGBRG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GBRG, }, }; const char *pixel_order_str[] = { "GRBG", "RGGB", "BGGR", "GBRG" }; @@ -526,6 +523,8 @@ static const struct v4l2_ctrl_ops smiapp_ctrl_ops = { static int smiapp_init_controls(struct smiapp_sensor *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned long *valid_link_freqs = &sensor->valid_link_freqs[ + sensor->csi_format->compressed - SMIAPP_COMPRESSED_BASE]; unsigned int max, i; int rval; @@ -608,8 +607,8 @@ static int smiapp_init_controls(struct smiapp_sensor *sensor) sensor->link_freq = v4l2_ctrl_new_int_menu( &sensor->src->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_LINK_FREQ, max, 0, - sensor->platform_data->op_sys_clock); + V4L2_CID_LINK_FREQ, __fls(*valid_link_freqs), + __ffs(*valid_link_freqs), sensor->platform_data->op_sys_clock); sensor->pixel_rate_csi = v4l2_ctrl_new_std( &sensor->src->ctrl_handler, &smiapp_ctrl_ops, @@ -745,6 +744,7 @@ static int smiapp_get_limits_binning(struct smiapp_sensor *sensor) static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct smiapp_pll *pll = &sensor->pll; unsigned int type, n; unsigned int i, pixel_order; int rval; @@ -808,14 +808,57 @@ static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor) dev_dbg(&client->dev, "jolly good! %d\n", j); sensor->default_mbus_frame_fmts |= 1 << j; - if (!sensor->csi_format - || f->width > sensor->csi_format->width - || (f->width == sensor->csi_format->width - && f->compressed - > sensor->csi_format->compressed)) { - sensor->csi_format = f; - sensor->internal_csi_format = f; - } + } + } + + /* Figure out which BPP values can be used with which formats. */ + pll->binning_horizontal = 1; + pll->binning_vertical = 1; + pll->scale_m = sensor->scale_m; + + for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) { + const struct smiapp_csi_data_format *f = + &smiapp_csi_data_formats[i]; + unsigned long *valid_link_freqs = + &sensor->valid_link_freqs[ + f->compressed - SMIAPP_COMPRESSED_BASE]; + unsigned int j; + + BUG_ON(f->compressed < SMIAPP_COMPRESSED_BASE); + BUG_ON(f->compressed > SMIAPP_COMPRESSED_MAX); + + if (!(sensor->default_mbus_frame_fmts & 1 << i)) + continue; + + pll->bits_per_pixel = f->compressed; + + for (j = 0; sensor->platform_data->op_sys_clock[j]; j++) { + pll->link_freq = sensor->platform_data->op_sys_clock[j]; + + rval = smiapp_pll_try(sensor, pll); + dev_dbg(&client->dev, "link freq %u Hz, bpp %u %s\n", + pll->link_freq, pll->bits_per_pixel, + rval ? "not ok" : "ok"); + if (rval) + continue; + + set_bit(j, valid_link_freqs); + } + + if (!*valid_link_freqs) { + dev_info(&client->dev, + "no valid link frequencies for %u bpp\n", + f->compressed); + sensor->default_mbus_frame_fmts &= ~BIT(i); + continue; + } + + if (!sensor->csi_format + || f->width > sensor->csi_format->width + || (f->width == sensor->csi_format->width + && f->compressed > sensor->csi_format->compressed)) { + sensor->csi_format = f; + sensor->internal_csi_format = f; } } @@ -904,7 +947,7 @@ static int smiapp_update_mode(struct smiapp_sensor *sensor) dev_dbg(&client->dev, "hblank\t\t%d\n", sensor->hblank->val); dev_dbg(&client->dev, "real timeperframe\t100/%d\n", - sensor->pll.vt_pix_clk_freq_hz / + sensor->pll.pixel_rate_pixel_array / ((sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width + sensor->hblank->val) * (sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height @@ -1687,51 +1730,77 @@ static const struct smiapp_csi_data_format return csi_format; } -static int smiapp_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_format *fmt) +static int smiapp_set_format_source(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) { struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - struct v4l2_rect *crops[SMIAPP_PADS]; + const struct smiapp_csi_data_format *csi_format, + *old_csi_format = sensor->csi_format; + unsigned long *valid_link_freqs; + u32 code = fmt->format.code; + unsigned int i; + int rval; - mutex_lock(&sensor->mutex); + rval = __smiapp_get_format(subdev, fh, fmt); + if (rval) + return rval; /* * Media bus code is changeable on src subdev's source pad. On * other source pads we just get format here. */ - if (fmt->pad == ssd->source_pad) { - u32 code = fmt->format.code; - int rval = __smiapp_get_format(subdev, fh, fmt); - bool range_changed = false; - unsigned int i; + if (subdev != &sensor->src->sd) + return 0; - if (!rval && subdev == &sensor->src->sd) { - const struct smiapp_csi_data_format *csi_format = - smiapp_validate_csi_data_format(sensor, code); + csi_format = smiapp_validate_csi_data_format(sensor, code); - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - if (csi_format->width != - sensor->csi_format->width) - range_changed = true; + fmt->format.code = csi_format->code; - sensor->csi_format = csi_format; - } - - fmt->format.code = csi_format->code; - } + if (fmt->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return 0; - mutex_unlock(&sensor->mutex); - if (rval || !range_changed) - return rval; + sensor->csi_format = csi_format; + if (csi_format->width != old_csi_format->width) for (i = 0; i < ARRAY_SIZE(sensor->test_data); i++) - v4l2_ctrl_modify_range( - sensor->test_data[i], - 0, (1 << sensor->csi_format->width) - 1, 1, 0); + __v4l2_ctrl_modify_range( + sensor->test_data[i], 0, + (1 << csi_format->width) - 1, 1, 0); + if (csi_format->compressed == old_csi_format->compressed) return 0; + + valid_link_freqs = + &sensor->valid_link_freqs[sensor->csi_format->compressed + - SMIAPP_COMPRESSED_BASE]; + + __v4l2_ctrl_modify_range( + sensor->link_freq, 0, + __fls(*valid_link_freqs), ~*valid_link_freqs, + __ffs(*valid_link_freqs)); + + return smiapp_pll_update(sensor); +} + +static int smiapp_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + struct v4l2_rect *crops[SMIAPP_PADS]; + + mutex_lock(&sensor->mutex); + + if (fmt->pad == ssd->source_pad) { + int rval; + + rval = smiapp_set_format_source(subdev, fh, fmt); + + mutex_unlock(&sensor->mutex); + + return rval; } /* Sink pad. Width and height are changeable here. */ @@ -2023,6 +2092,11 @@ static int __smiapp_sel_supported(struct v4l2_subdev *subdev, == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) return 0; return -EINVAL; + case V4L2_SEL_TGT_NATIVE_SIZE: + if (ssd == sensor->pixel_array + && sel->pad == SMIAPP_PA_PAD_SRC) + return 0; + return -EINVAL; case V4L2_SEL_TGT_COMPOSE: case V4L2_SEL_TGT_COMPOSE_BOUNDS: if (sel->pad == ssd->source_pad) @@ -2121,7 +2195,9 @@ static int __smiapp_get_selection(struct v4l2_subdev *subdev, switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_NATIVE_SIZE: if (ssd == sensor->pixel_array) { + sel->r.left = sel->r.top = 0; sel->r.width = sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; sel->r.height = @@ -2482,12 +2558,6 @@ static int smiapp_registered(struct v4l2_subdev *subdev) goto out_power_off; } - rval = smiapp_get_mbus_formats(sensor); - if (rval) { - rval = -ENODEV; - goto out_power_off; - } - if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY]) { u32 val; @@ -2569,6 +2639,22 @@ static int smiapp_registered(struct v4l2_subdev *subdev) sensor->scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; + /* prepare PLL configuration input values */ + 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); + pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; + /* 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; + + rval = smiapp_get_mbus_formats(sensor); + if (rval) { + rval = -ENODEV; + goto out_nvm_release; + } + for (i = 0; i < SMIAPP_SUBDEVS; i++) { struct { struct smiapp_subdev *ssd; @@ -2666,18 +2752,9 @@ static int smiapp_registered(struct v4l2_subdev *subdev) if (rval < 0) goto out_nvm_release; - /* prepare PLL configuration input values */ - 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; - pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; - + mutex_lock(&sensor->mutex); rval = smiapp_update_mode(sensor); + mutex_unlock(&sensor->mutex); if (rval) { dev_err(&client->dev, "update mode failed\n"); goto out_nvm_release; diff --git a/drivers/media/i2c/smiapp/smiapp.h b/drivers/media/i2c/smiapp/smiapp.h index 874b49ffd88f..f88f8ec344d3 100644 --- a/drivers/media/i2c/smiapp/smiapp.h +++ b/drivers/media/i2c/smiapp/smiapp.h @@ -156,6 +156,11 @@ struct smiapp_csi_data_format { #define SMIAPP_PAD_SRC 1 #define SMIAPP_PADS 2 +#define SMIAPP_COMPRESSED_BASE 8 +#define SMIAPP_COMPRESSED_MAX 12 +#define SMIAPP_NR_OF_COMPRESSED (SMIAPP_COMPRESSED_MAX - \ + SMIAPP_COMPRESSED_BASE + 1) + struct smiapp_binning_subtype { u8 horizontal:4; u8 vertical:4; @@ -232,6 +237,9 @@ struct smiapp_sensor { struct smiapp_pll pll; + /* Is a default format supported for a given BPP? */ + unsigned long valid_link_freqs[SMIAPP_NR_OF_COMPRESSED]; + /* Pixel array controls */ struct v4l2_ctrl *analog_gain; struct v4l2_ctrl *exposure; |