diff options
Diffstat (limited to 'drivers/media/i2c/tc358746.c')
| -rw-r--r-- | drivers/media/i2c/tc358746.c | 312 |
1 files changed, 166 insertions, 146 deletions
diff --git a/drivers/media/i2c/tc358746.c b/drivers/media/i2c/tc358746.c index 3f7e147ef594..86d9ba3ea4e5 100644 --- a/drivers/media/i2c/tc358746.c +++ b/drivers/media/i2c/tc358746.c @@ -161,10 +161,6 @@ struct tc358746 { u16 pll_pre_div; u16 pll_mul; -#define TC358746_VB_MAX_SIZE (511 * 32) -#define TC358746_VB_DEFAULT_SIZE (1 * 32) - unsigned int vb_size; /* Video buffer size in bits */ - struct phy_configure_opts_mipi_dphy dphy_cfg; }; @@ -202,6 +198,15 @@ enum { PDFORMAT_YUV444, }; +#define TC358746_FORMAT_RAW(_bpp, _code) \ +{ \ + .code = _code, \ + .bus_width = _bpp, \ + .bpp = _bpp, \ + .pdformat = PDFORMAT_RAW##_bpp, \ + .pdataf = PDATAF_MODE0, /* don't care */ \ +} + /* Check tc358746_src_mbus_code() if you add new formats */ static const struct tc358746_format tc358746_formats[] = { { @@ -230,7 +235,23 @@ static const struct tc358746_format tc358746_formats[] = { .bpp = 20, .pdformat = PDFORMAT_YUV422_10BIT, .pdataf = PDATAF_MODE0, /* don't care */ - } + }, + TC358746_FORMAT_RAW(8, MEDIA_BUS_FMT_SBGGR8_1X8), + TC358746_FORMAT_RAW(8, MEDIA_BUS_FMT_SGBRG8_1X8), + TC358746_FORMAT_RAW(8, MEDIA_BUS_FMT_SGRBG8_1X8), + TC358746_FORMAT_RAW(8, MEDIA_BUS_FMT_SRGGB8_1X8), + TC358746_FORMAT_RAW(10, MEDIA_BUS_FMT_SBGGR10_1X10), + TC358746_FORMAT_RAW(10, MEDIA_BUS_FMT_SGBRG10_1X10), + TC358746_FORMAT_RAW(10, MEDIA_BUS_FMT_SGRBG10_1X10), + TC358746_FORMAT_RAW(10, MEDIA_BUS_FMT_SRGGB10_1X10), + TC358746_FORMAT_RAW(12, MEDIA_BUS_FMT_SBGGR12_1X12), + TC358746_FORMAT_RAW(12, MEDIA_BUS_FMT_SGBRG12_1X12), + TC358746_FORMAT_RAW(12, MEDIA_BUS_FMT_SGRBG12_1X12), + TC358746_FORMAT_RAW(12, MEDIA_BUS_FMT_SRGGB12_1X12), + TC358746_FORMAT_RAW(14, MEDIA_BUS_FMT_SBGGR14_1X14), + TC358746_FORMAT_RAW(14, MEDIA_BUS_FMT_SGBRG14_1X14), + TC358746_FORMAT_RAW(14, MEDIA_BUS_FMT_SGRBG14_1X14), + TC358746_FORMAT_RAW(14, MEDIA_BUS_FMT_SRGGB14_1X14), }; /* Get n-th format for pad */ @@ -415,6 +436,70 @@ tc358746_apply_pll_config(struct tc358746 *tc358746) return tc358746_set_bits(tc358746, PLLCTL1_REG, CKEN); } +#define TC358746_VB_PRECISION 10 +#define TC358746_VB_MAX_SIZE (511 * 32) +#define TC358746_VB_DEFAULT_SIZE (1 * 32) + +static int tc358746_calc_vb_size(struct tc358746 *tc358746, + s64 source_link_freq, + const struct v4l2_mbus_framefmt *mbusfmt, + const struct tc358746_format *fmt) +{ + unsigned long csi_bitrate, source_bitrate; + unsigned int fifo_sz, tmp, n; + int vb_size; /* Video buffer size in bits */ + + source_bitrate = source_link_freq * fmt->bus_width; + + csi_bitrate = tc358746->dphy_cfg.lanes * tc358746->pll_rate; + + dev_dbg(tc358746->sd.dev, + "Fifo settings params: source-bitrate:%lu csi-bitrate:%lu", + source_bitrate, csi_bitrate); + + /* Avoid possible FIFO overflows */ + if (csi_bitrate < source_bitrate) + return -EINVAL; + + /* Best case */ + if (csi_bitrate == source_bitrate) { + fifo_sz = TC358746_VB_DEFAULT_SIZE; + vb_size = TC358746_VB_DEFAULT_SIZE; + } else { + /* + * Avoid possible FIFO underflow in case of + * csi_bitrate > source_bitrate. For such case the chip has a internal + * fifo which can be used to delay the line output. + * + * Fifo size calculation (excluding precision): + * + * fifo-sz, image-width - in bits + * sbr - source_bitrate in bits/s + * csir - csi_bitrate in bits/s + * + * image-width / csir >= (image-width - fifo-sz) / sbr + * image-width * sbr / csir >= image-width - fifo-sz + * fifo-sz >= image-width - image-width * sbr / csir; with n = csir/sbr + * fifo-sz >= image-width - image-width / n + */ + source_bitrate /= TC358746_VB_PRECISION; + n = csi_bitrate / source_bitrate; + tmp = (mbusfmt->width * TC358746_VB_PRECISION) / n; + fifo_sz = mbusfmt->width - tmp; + fifo_sz *= fmt->bpp; + vb_size = round_up(fifo_sz, 32); + } + + dev_dbg(tc358746->sd.dev, + "Found FIFO size[bits]:%u -> aligned to size[bits]:%u\n", + fifo_sz, vb_size); + + if (vb_size > TC358746_VB_MAX_SIZE) + return -EINVAL; + + return vb_size; +} + static int tc358746_apply_misc_config(struct tc358746 *tc358746) { const struct v4l2_mbus_framefmt *mbusfmt; @@ -422,14 +507,32 @@ static int tc358746_apply_misc_config(struct tc358746 *tc358746) struct v4l2_subdev_state *sink_state; const struct tc358746_format *fmt; struct device *dev = sd->dev; + struct media_pad *source_pad; + s64 source_link_freq; + int vb_size; u32 val; int err; sink_state = v4l2_subdev_lock_and_get_active_state(sd); - mbusfmt = v4l2_subdev_get_pad_format(sd, sink_state, TC358746_SINK); + mbusfmt = v4l2_subdev_state_get_format(sink_state, TC358746_SINK); fmt = tc358746_get_format_by_code(TC358746_SINK, mbusfmt->code); + source_pad = media_entity_remote_source_pad_unique(&sd->entity); + if (IS_ERR(source_pad)) { + dev_err(dev, "Failed to get source pad of %s\n", sd->name); + err = PTR_ERR(source_pad); + goto out; + } + source_link_freq = v4l2_get_link_freq(source_pad, 0, 0); + if (source_link_freq <= 0) { + dev_err(dev, + "Failed to query or invalid source link frequency\n"); + /* Return -EINVAL in case of source_link_freq is 0 */ + err = source_link_freq ?: -EINVAL; + goto out; + } + /* Self defined CSI user data type id's are not supported yet */ val = PDFMT(fmt->pdformat); dev_dbg(dev, "DATAFMT: 0x%x\n", val); @@ -443,7 +546,13 @@ static int tc358746_apply_misc_config(struct tc358746 *tc358746) if (err) goto out; - val = tc358746->vb_size / 32; + vb_size = tc358746_calc_vb_size(tc358746, source_link_freq, mbusfmt, fmt); + if (vb_size < 0) { + err = vb_size; + goto out; + } + + val = vb_size / 32; dev_dbg(dev, "FIFOCTL: %u (0x%x)\n", val, val); err = tc358746_write(tc358746, FIFOCTL_REG, val); if (err) @@ -460,24 +569,20 @@ out: return err; } -/* Use MHz as base so the div needs no u64 */ -static u32 tc358746_cfg_to_cnt(unsigned int cfg_val, - unsigned int clk_mhz, - unsigned int time_base) +static u32 tc358746_cfg_to_cnt(unsigned long cfg_val, unsigned long clk_hz, + unsigned long long time_base) { - return DIV_ROUND_UP(cfg_val * clk_mhz, time_base); + return div64_u64((u64)cfg_val * clk_hz + time_base - 1, time_base); } -static u32 tc358746_ps_to_cnt(unsigned int cfg_val, - unsigned int clk_mhz) +static u32 tc358746_ps_to_cnt(unsigned long cfg_val, unsigned long clk_hz) { - return tc358746_cfg_to_cnt(cfg_val, clk_mhz, USEC_PER_SEC); + return tc358746_cfg_to_cnt(cfg_val, clk_hz, PSEC_PER_SEC); } -static u32 tc358746_us_to_cnt(unsigned int cfg_val, - unsigned int clk_mhz) +static u32 tc358746_us_to_cnt(unsigned long cfg_val, unsigned long clk_hz) { - return tc358746_cfg_to_cnt(cfg_val, clk_mhz, 1); + return tc358746_cfg_to_cnt(cfg_val, clk_hz, USEC_PER_SEC); } static int tc358746_apply_dphy_config(struct tc358746 *tc358746) @@ -492,7 +597,6 @@ static int tc358746_apply_dphy_config(struct tc358746 *tc358746) /* The hs_byte_clk is also called SYSCLK in the excel sheet */ hs_byte_clk = cfg->hs_clk_rate / 8; - hs_byte_clk /= HZ_PER_MHZ; hf_clk = hs_byte_clk / 2; val = tc358746_us_to_cnt(cfg->init, hf_clk) - 1; @@ -712,7 +816,6 @@ static int tc358746_s_stream(struct v4l2_subdev *sd, int enable) return 0; err_out: - pm_runtime_mark_last_busy(sd->dev); pm_runtime_put_sync_autosuspend(sd->dev); return err; @@ -734,21 +837,20 @@ err_out: if (err) return err; - pm_runtime_mark_last_busy(sd->dev); pm_runtime_put_sync_autosuspend(sd->dev); return v4l2_subdev_call(src, video, s_stream, 0); } -static int tc358746_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) +static int tc358746_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { struct v4l2_mbus_framefmt *fmt; - fmt = v4l2_subdev_get_pad_format(sd, state, TC358746_SINK); + fmt = v4l2_subdev_state_get_format(state, TC358746_SINK); *fmt = tc358746_def_fmt; - fmt = v4l2_subdev_get_pad_format(sd, state, TC358746_SOURCE); + fmt = v4l2_subdev_state_get_format(state, TC358746_SOURCE); *fmt = tc358746_def_fmt; fmt->code = tc358746_src_mbus_code(tc358746_def_fmt.code); @@ -781,11 +883,15 @@ static int tc358746_set_fmt(struct v4l2_subdev *sd, if (format->pad == TC358746_SOURCE) return v4l2_subdev_get_fmt(sd, sd_state, format); - sink_fmt = v4l2_subdev_get_pad_format(sd, sd_state, TC358746_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, TC358746_SINK); fmt = tc358746_get_format_by_code(format->pad, format->format.code); - if (IS_ERR(fmt)) + if (IS_ERR(fmt)) { fmt = tc358746_get_format_by_code(format->pad, tc358746_def_fmt.code); + // Can't happen, but just in case... + if (WARN_ON(IS_ERR(fmt))) + return -EINVAL; + } format->format.code = fmt->code; format->format.field = V4L2_FIELD_NONE; @@ -796,7 +902,7 @@ static int tc358746_set_fmt(struct v4l2_subdev *sd, *sink_fmt = format->format; - src_fmt = v4l2_subdev_get_pad_format(sd, sd_state, TC358746_SOURCE); + src_fmt = v4l2_subdev_state_get_format(sd_state, TC358746_SOURCE); *src_fmt = *sink_fmt; src_fmt->code = tc358746_src_mbus_code(sink_fmt->code); @@ -839,14 +945,13 @@ static unsigned long tc358746_find_pll_settings(struct tc358746 *tc358746, if (fin < 4 * HZ_PER_MHZ || fin > 40 * HZ_PER_MHZ) continue; - tmp = fout * p * postdiv; - do_div(tmp, fin); - mul = tmp; + tmp = fout * postdiv; + mul = div64_ul(tmp, fin); if (mul > 511) continue; tmp = mul * fin; - do_div(tmp, p * postdiv); + do_div(tmp, postdiv); delta = abs(fout - tmp); if (delta < min_delta) { @@ -879,97 +984,6 @@ static unsigned long tc358746_find_pll_settings(struct tc358746 *tc358746, return best_freq; } -#define TC358746_PRECISION 10 - -static int -tc358746_link_validate(struct v4l2_subdev *sd, struct media_link *link, - struct v4l2_subdev_format *source_fmt, - struct v4l2_subdev_format *sink_fmt) -{ - struct tc358746 *tc358746 = to_tc358746(sd); - unsigned long csi_bitrate, source_bitrate; - struct v4l2_subdev_state *sink_state; - struct v4l2_mbus_framefmt *mbusfmt; - const struct tc358746_format *fmt; - unsigned int fifo_sz, tmp, n; - struct v4l2_subdev *source; - s64 source_link_freq; - int err; - - err = v4l2_subdev_link_validate_default(sd, link, source_fmt, sink_fmt); - if (err) - return err; - - sink_state = v4l2_subdev_lock_and_get_active_state(sd); - mbusfmt = v4l2_subdev_get_pad_format(sd, sink_state, TC358746_SINK); - - /* Check the FIFO settings */ - fmt = tc358746_get_format_by_code(TC358746_SINK, mbusfmt->code); - - source = media_entity_to_v4l2_subdev(link->source->entity); - source_link_freq = v4l2_get_link_freq(source->ctrl_handler, 0, 0); - if (source_link_freq <= 0) { - dev_err(tc358746->sd.dev, - "Failed to query or invalid source link frequency\n"); - v4l2_subdev_unlock_state(sink_state); - /* Return -EINVAL in case of source_link_freq is 0 */ - return source_link_freq ? : -EINVAL; - } - source_bitrate = source_link_freq * fmt->bus_width; - - csi_bitrate = tc358746->dphy_cfg.lanes * tc358746->pll_rate; - - dev_dbg(tc358746->sd.dev, - "Fifo settings params: source-bitrate:%lu csi-bitrate:%lu", - source_bitrate, csi_bitrate); - - /* Avoid possible FIFO overflows */ - if (csi_bitrate < source_bitrate) { - v4l2_subdev_unlock_state(sink_state); - return -EINVAL; - } - - /* Best case */ - if (csi_bitrate == source_bitrate) { - fifo_sz = TC358746_VB_DEFAULT_SIZE; - tc358746->vb_size = TC358746_VB_DEFAULT_SIZE; - goto out; - } - - /* - * Avoid possible FIFO underflow in case of - * csi_bitrate > source_bitrate. For such case the chip has a internal - * fifo which can be used to delay the line output. - * - * Fifo size calculation (excluding precision): - * - * fifo-sz, image-width - in bits - * sbr - source_bitrate in bits/s - * csir - csi_bitrate in bits/s - * - * image-width / csir >= (image-width - fifo-sz) / sbr - * image-width * sbr / csir >= image-width - fifo-sz - * fifo-sz >= image-width - image-width * sbr / csir; with n = csir/sbr - * fifo-sz >= image-width - image-width / n - */ - - source_bitrate /= TC358746_PRECISION; - n = csi_bitrate / source_bitrate; - tmp = (mbusfmt->width * TC358746_PRECISION) / n; - fifo_sz = mbusfmt->width - tmp; - fifo_sz *= fmt->bpp; - tc358746->vb_size = round_up(fifo_sz, 32); - -out: - dev_dbg(tc358746->sd.dev, - "Found FIFO size[bits]:%u -> aligned to size[bits]:%u\n", - fifo_sz, tc358746->vb_size); - - v4l2_subdev_unlock_state(sink_state); - - return tc358746->vb_size > TC358746_VB_MAX_SIZE ? -EINVAL : 0; -} - static int tc358746_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_config *config) { @@ -1000,7 +1014,6 @@ tc358746_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) err = tc358746_read(tc358746, reg->reg, &val); reg->val = val; - pm_runtime_mark_last_busy(sd->dev); pm_runtime_put_sync_autosuspend(sd->dev); return err; @@ -1016,7 +1029,6 @@ tc358746_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) tc358746_write(tc358746, (u32)reg->reg, (u32)reg->val); - pm_runtime_mark_last_busy(sd->dev); pm_runtime_put_sync_autosuspend(sd->dev); return 0; @@ -1034,11 +1046,10 @@ static const struct v4l2_subdev_video_ops tc358746_video_ops = { }; static const struct v4l2_subdev_pad_ops tc358746_pad_ops = { - .init_cfg = tc358746_init_cfg, .enum_mbus_code = tc358746_enum_mbus_code, .set_fmt = tc358746_set_fmt, .get_fmt = v4l2_subdev_get_fmt, - .link_validate = tc358746_link_validate, + .link_validate = v4l2_subdev_link_validate_default, .get_mbus_config = tc358746_get_mbus_config, }; @@ -1048,6 +1059,10 @@ static const struct v4l2_subdev_ops tc358746_ops = { .pad = &tc358746_pad_ops, }; +static const struct v4l2_subdev_internal_ops tc358746_internal_ops = { + .init_state = tc358746_init_state, +}; + static const struct media_entity_operations tc358746_entity_ops = { .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1, .link_validate = v4l2_subdev_link_validate, @@ -1207,14 +1222,16 @@ tc358746_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) return tc358746->pll_rate / (prediv * postdiv); } -static long tc358746_mclk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int tc358746_mclk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct tc358746 *tc358746 = clk_hw_to_tc358746(hw); - *parent_rate = tc358746->pll_rate; + req->best_parent_rate = tc358746->pll_rate; + + req->rate = tc358746_find_mclk_settings(tc358746, req->rate); - return tc358746_find_mclk_settings(tc358746, rate); + return 0; } static int tc358746_mclk_set_rate(struct clk_hw *hw, unsigned long rate, @@ -1231,7 +1248,7 @@ static const struct clk_ops tc358746_mclk_ops = { .enable = tc358746_mclk_enable, .disable = tc358746_mclk_disable, .recalc_rate = tc358746_recalc_rate, - .round_rate = tc358746_mclk_round_rate, + .determine_rate = tc358746_mclk_determine_rate, .set_rate = tc358746_mclk_set_rate, }; @@ -1278,6 +1295,7 @@ tc358746_init_subdev(struct tc358746 *tc358746, struct i2c_client *client) int err; v4l2_i2c_subdev_init(sd, client, &tc358746_ops); + sd->internal_ops = &tc358746_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; sd->entity.ops = &tc358746_entity_ops; @@ -1345,8 +1363,6 @@ tc358746_init_output_port(struct tc358746 *tc358746, unsigned long refclk) if (err) goto err; - tc358746->vb_size = TC358746_VB_DEFAULT_SIZE; - return 0; err: @@ -1377,7 +1393,6 @@ static int tc358746_init_hw(struct tc358746 *tc358746) } err = tc358746_read(tc358746, CHIPID_REG, &val); - pm_runtime_mark_last_busy(dev); pm_runtime_put_sync_autosuspend(dev); if (err) return -ENODEV; @@ -1426,7 +1441,7 @@ static int tc358746_init_controls(struct tc358746 *tc358746) static int tc358746_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct tc358746 *tc358746 = container_of(notifier, struct tc358746, notifier); @@ -1445,7 +1460,7 @@ static int tc358746_async_register(struct tc358746 *tc358746) struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_PARALLEL, }; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct fwnode_handle *ep; int err; @@ -1460,9 +1475,9 @@ static int tc358746_async_register(struct tc358746 *tc358746) return err; } - v4l2_async_nf_init(&tc358746->notifier); + v4l2_async_subdev_nf_init(&tc358746->notifier, &tc358746->sd); asd = v4l2_async_nf_add_fwnode_remote(&tc358746->notifier, ep, - struct v4l2_async_subdev); + struct v4l2_async_connection); fwnode_handle_put(ep); if (IS_ERR(asd)) { @@ -1472,13 +1487,10 @@ static int tc358746_async_register(struct tc358746 *tc358746) tc358746->notifier.ops = &tc358746_notify_ops; - err = v4l2_async_subdev_nf_register(&tc358746->sd, &tc358746->notifier); + err = v4l2_async_nf_register(&tc358746->notifier); if (err) goto err_cleanup; - tc358746->sd.fwnode = fwnode_graph_get_endpoint_by_id( - dev_fwnode(tc358746->sd.dev), TC358746_SOURCE, 0, 0); - err = v4l2_async_register_subdev(&tc358746->sd); if (err) goto err_unregister; @@ -1486,7 +1498,6 @@ static int tc358746_async_register(struct tc358746 *tc358746) return 0; err_unregister: - fwnode_handle_put(tc358746->sd.fwnode); v4l2_async_nf_unregister(&tc358746->notifier); err_cleanup: v4l2_async_nf_cleanup(&tc358746->notifier); @@ -1605,7 +1616,6 @@ static void tc358746_remove(struct i2c_client *client) v4l2_fwnode_endpoint_free(&tc358746->csi_vep); v4l2_async_nf_unregister(&tc358746->notifier); v4l2_async_nf_cleanup(&tc358746->notifier); - fwnode_handle_put(sd->fwnode); v4l2_async_unregister_subdev(sd); media_entity_cleanup(&sd->entity); @@ -1614,6 +1624,16 @@ static void tc358746_remove(struct i2c_client *client) pm_runtime_dont_use_autosuspend(sd->dev); } +/* + * This function has been created just to avoid a smatch warning, + * please do not merge it into tc358746_suspend until you have + * confirmed that it does not introduce a new warning. + */ +static void tc358746_clk_enable(struct tc358746 *tc358746) +{ + clk_prepare_enable(tc358746->refclk); +} + static int tc358746_suspend(struct device *dev) { struct tc358746 *tc358746 = dev_get_drvdata(dev); @@ -1624,7 +1644,7 @@ static int tc358746_suspend(struct device *dev) err = regulator_bulk_disable(ARRAY_SIZE(tc358746_supplies), tc358746->supplies); if (err) - clk_prepare_enable(tc358746->refclk); + tc358746_clk_enable(tc358746); return err; } |
