diff options
Diffstat (limited to 'drivers/media/i2c/mt9m114.c')
-rw-r--r-- | drivers/media/i2c/mt9m114.c | 252 |
1 files changed, 159 insertions, 93 deletions
diff --git a/drivers/media/i2c/mt9m114.c b/drivers/media/i2c/mt9m114.c index 5f0b0ad8f885..51ebbe7ae996 100644 --- a/drivers/media/i2c/mt9m114.c +++ b/drivers/media/i2c/mt9m114.c @@ -18,6 +18,7 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/pm_runtime.h> +#include <linux/property.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/types.h> @@ -42,6 +43,9 @@ #define MT9M114_RESET_AND_MISC_CONTROL CCI_REG16(0x001a) #define MT9M114_RESET_SOC BIT(0) #define MT9M114_PAD_SLEW CCI_REG16(0x001e) +#define MT9M114_PAD_SLEW_MIN 0 +#define MT9M114_PAD_SLEW_MAX 7 +#define MT9M114_PAD_SLEW_DEFAULT 7 #define MT9M114_PAD_CONTROL CCI_REG16(0x0032) /* XDMA registers */ @@ -261,6 +265,7 @@ #define MT9M114_CAM_PGA_PGA_CONTROL CCI_REG16(0xc95e) #define MT9M114_CAM_SYSCTL_PLL_ENABLE CCI_REG8(0xc97e) #define MT9M114_CAM_SYSCTL_PLL_ENABLE_VALUE BIT(0) +#define MT9M114_CAM_SYSCTL_PLL_DISABLE_VALUE 0x00 #define MT9M114_CAM_SYSCTL_PLL_DIVIDER_M_N CCI_REG16(0xc980) #define MT9M114_CAM_SYSCTL_PLL_DIVIDER_VALUE(m, n) (((n) << 8) | (m)) #define MT9M114_CAM_SYSCTL_PLL_DIVIDER_P CCI_REG16(0xc982) @@ -377,6 +382,7 @@ struct mt9m114 { struct gpio_desc *reset; struct regulator_bulk_data supplies[3]; struct v4l2_fwnode_endpoint bus_cfg; + bool bypass_pll; struct { unsigned int m; @@ -386,6 +392,7 @@ struct mt9m114 { unsigned int pixrate; bool streaming; + u32 pad_slew_rate; /* Pixel Array */ struct { @@ -643,9 +650,6 @@ static const struct cci_reg_sequence mt9m114_init[] = { { MT9M114_CAM_SENSOR_CFG_FINE_INTEG_TIME_MAX, 1459 }, { MT9M114_CAM_SENSOR_CFG_FINE_CORRECTION, 96 }, { MT9M114_CAM_SENSOR_CFG_REG_0_DATA, 32 }, - - /* Miscellaneous settings */ - { MT9M114_PAD_SLEW, 0x0777 }, }; /* ----------------------------------------------------------------------------- @@ -743,14 +747,21 @@ static int mt9m114_initialize(struct mt9m114 *sensor) } /* Configure the PLL. */ - cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_ENABLE, - MT9M114_CAM_SYSCTL_PLL_ENABLE_VALUE, &ret); - cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_DIVIDER_M_N, - MT9M114_CAM_SYSCTL_PLL_DIVIDER_VALUE(sensor->pll.m, - sensor->pll.n), - &ret); - cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_DIVIDER_P, - MT9M114_CAM_SYSCTL_PLL_DIVIDER_P_VALUE(sensor->pll.p), &ret); + if (sensor->bypass_pll) { + cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_ENABLE, + MT9M114_CAM_SYSCTL_PLL_DISABLE_VALUE, &ret); + } else { + cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_ENABLE, + MT9M114_CAM_SYSCTL_PLL_ENABLE_VALUE, &ret); + cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_DIVIDER_M_N, + MT9M114_CAM_SYSCTL_PLL_DIVIDER_VALUE(sensor->pll.m, + sensor->pll.n), + &ret); + cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_DIVIDER_P, + MT9M114_CAM_SYSCTL_PLL_DIVIDER_P_VALUE(sensor->pll.p), + &ret); + } + cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_PIXCLK, sensor->pixrate, &ret); @@ -770,6 +781,13 @@ static int mt9m114_initialize(struct mt9m114 *sensor) if (ret < 0) return ret; + value = sensor->pad_slew_rate + | sensor->pad_slew_rate << 4 + | sensor->pad_slew_rate << 8; + cci_write(sensor->regmap, MT9M114_PAD_SLEW, value, &ret); + if (ret < 0) + return ret; + ret = mt9m114_set_state(sensor, MT9M114_SYS_STATE_ENTER_CONFIG_CHANGE); if (ret < 0) return ret; @@ -781,41 +799,25 @@ static int mt9m114_initialize(struct mt9m114 *sensor) return 0; } -static int mt9m114_configure(struct mt9m114 *sensor, - struct v4l2_subdev_state *pa_state, - struct v4l2_subdev_state *ifp_state) +static int mt9m114_configure_pa(struct mt9m114 *sensor, + struct v4l2_subdev_state *state) { - const struct v4l2_mbus_framefmt *pa_format; - const struct v4l2_rect *pa_crop; - const struct mt9m114_format_info *ifp_info; - const struct v4l2_mbus_framefmt *ifp_format; - const struct v4l2_rect *ifp_crop; - const struct v4l2_rect *ifp_compose; + const struct v4l2_mbus_framefmt *format; + const struct v4l2_rect *crop; unsigned int hratio, vratio; - u64 output_format; u64 read_mode; - int ret = 0; - - pa_format = v4l2_subdev_state_get_format(pa_state, 0); - pa_crop = v4l2_subdev_state_get_crop(pa_state, 0); + int ret; - ifp_format = v4l2_subdev_state_get_format(ifp_state, 1); - ifp_info = mt9m114_format_info(sensor, 1, ifp_format->code); - ifp_crop = v4l2_subdev_state_get_crop(ifp_state, 0); - ifp_compose = v4l2_subdev_state_get_compose(ifp_state, 0); + format = v4l2_subdev_state_get_format(state, 0); + crop = v4l2_subdev_state_get_crop(state, 0); ret = cci_read(sensor->regmap, MT9M114_CAM_SENSOR_CONTROL_READ_MODE, &read_mode, NULL); if (ret < 0) return ret; - ret = cci_read(sensor->regmap, MT9M114_CAM_OUTPUT_FORMAT, - &output_format, NULL); - if (ret < 0) - return ret; - - hratio = pa_crop->width / pa_format->width; - vratio = pa_crop->height / pa_format->height; + hratio = crop->width / format->width; + vratio = crop->height / format->height; /* * Pixel array crop and binning. The CAM_SENSOR_CFG_CPIPE_LAST_ROW @@ -824,15 +826,15 @@ static int mt9m114_configure(struct mt9m114 *sensor, * example sensor modes. */ cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_X_ADDR_START, - pa_crop->left, &ret); + crop->left, &ret); cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_Y_ADDR_START, - pa_crop->top, &ret); + crop->top, &ret); cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_X_ADDR_END, - pa_crop->width + pa_crop->left - 1, &ret); + crop->width + crop->left - 1, &ret); cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_Y_ADDR_END, - pa_crop->height + pa_crop->top - 1, &ret); + crop->height + crop->top - 1, &ret); cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_CPIPE_LAST_ROW, - (pa_crop->height - 4) / vratio - 1, &ret); + (crop->height - 4) / vratio - 1, &ret); read_mode &= ~(MT9M114_CAM_SENSOR_CONTROL_X_READ_OUT_MASK | MT9M114_CAM_SENSOR_CONTROL_Y_READ_OUT_MASK); @@ -845,6 +847,29 @@ static int mt9m114_configure(struct mt9m114 *sensor, cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CONTROL_READ_MODE, read_mode, &ret); + return ret; +} + +static int mt9m114_configure_ifp(struct mt9m114 *sensor, + struct v4l2_subdev_state *state) +{ + const struct mt9m114_format_info *info; + const struct v4l2_mbus_framefmt *format; + const struct v4l2_rect *crop; + const struct v4l2_rect *compose; + u64 output_format; + int ret = 0; + + format = v4l2_subdev_state_get_format(state, 1); + info = mt9m114_format_info(sensor, 1, format->code); + crop = v4l2_subdev_state_get_crop(state, 0); + compose = v4l2_subdev_state_get_compose(state, 0); + + ret = cci_read(sensor->regmap, MT9M114_CAM_OUTPUT_FORMAT, + &output_format, NULL); + if (ret < 0) + return ret; + /* * Color pipeline (IFP) cropping and scaling. Subtract 4 from the left * and top coordinates to compensate for the lines and columns removed @@ -852,18 +877,18 @@ static int mt9m114_configure(struct mt9m114 *sensor, * not in the hardware. */ cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_XOFFSET, - ifp_crop->left - 4, &ret); + crop->left - 4, &ret); cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_YOFFSET, - ifp_crop->top - 4, &ret); + crop->top - 4, &ret); cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_WIDTH, - ifp_crop->width, &ret); + crop->width, &ret); cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_HEIGHT, - ifp_crop->height, &ret); + crop->height, &ret); cci_write(sensor->regmap, MT9M114_CAM_OUTPUT_WIDTH, - ifp_compose->width, &ret); + compose->width, &ret); cci_write(sensor->regmap, MT9M114_CAM_OUTPUT_HEIGHT, - ifp_compose->height, &ret); + compose->height, &ret); /* AWB and AE windows, use the full frame. */ cci_write(sensor->regmap, MT9M114_CAM_STAT_AWB_CLIP_WINDOW_XSTART, @@ -871,18 +896,18 @@ static int mt9m114_configure(struct mt9m114 *sensor, cci_write(sensor->regmap, MT9M114_CAM_STAT_AWB_CLIP_WINDOW_YSTART, 0, &ret); cci_write(sensor->regmap, MT9M114_CAM_STAT_AWB_CLIP_WINDOW_XEND, - ifp_compose->width - 1, &ret); + compose->width - 1, &ret); cci_write(sensor->regmap, MT9M114_CAM_STAT_AWB_CLIP_WINDOW_YEND, - ifp_compose->height - 1, &ret); + compose->height - 1, &ret); cci_write(sensor->regmap, MT9M114_CAM_STAT_AE_INITIAL_WINDOW_XSTART, 0, &ret); cci_write(sensor->regmap, MT9M114_CAM_STAT_AE_INITIAL_WINDOW_YSTART, 0, &ret); cci_write(sensor->regmap, MT9M114_CAM_STAT_AE_INITIAL_WINDOW_XEND, - ifp_compose->width / 5 - 1, &ret); + compose->width / 5 - 1, &ret); cci_write(sensor->regmap, MT9M114_CAM_STAT_AE_INITIAL_WINDOW_YEND, - ifp_compose->height / 5 - 1, &ret); + compose->height / 5 - 1, &ret); cci_write(sensor->regmap, MT9M114_CAM_CROP_CROPMODE, MT9M114_CAM_CROP_MODE_AWB_AUTO_CROP_EN | @@ -894,7 +919,7 @@ static int mt9m114_configure(struct mt9m114 *sensor, MT9M114_CAM_OUTPUT_FORMAT_FORMAT_MASK | MT9M114_CAM_OUTPUT_FORMAT_SWAP_BYTES | MT9M114_CAM_OUTPUT_FORMAT_SWAP_RED_BLUE); - output_format |= ifp_info->output_format; + output_format |= info->output_format; cci_write(sensor->regmap, MT9M114_CAM_OUTPUT_FORMAT, output_format, &ret); @@ -925,7 +950,11 @@ static int mt9m114_start_streaming(struct mt9m114 *sensor, if (ret) return ret; - ret = mt9m114_configure(sensor, pa_state, ifp_state); + ret = mt9m114_configure_ifp(sensor, ifp_state); + if (ret) + goto error; + + ret = mt9m114_configure_pa(sensor, pa_state); if (ret) goto error; @@ -954,7 +983,6 @@ static int mt9m114_start_streaming(struct mt9m114 *sensor, return 0; error: - pm_runtime_mark_last_busy(&sensor->client->dev); pm_runtime_put_autosuspend(&sensor->client->dev); return ret; @@ -968,7 +996,6 @@ static int mt9m114_stop_streaming(struct mt9m114 *sensor) ret = mt9m114_set_state(sensor, MT9M114_SYS_STATE_ENTER_SUSPEND); - pm_runtime_mark_last_busy(&sensor->client->dev); pm_runtime_put_autosuspend(&sensor->client->dev); return ret; @@ -1026,7 +1053,6 @@ static int mt9m114_pa_g_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_mark_last_busy(&sensor->client->dev); pm_runtime_put_autosuspend(&sensor->client->dev); return ret; @@ -1093,7 +1119,6 @@ static int mt9m114_pa_s_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_mark_last_busy(&sensor->client->dev); pm_runtime_put_autosuspend(&sensor->client->dev); return ret; @@ -1267,6 +1292,7 @@ static int mt9m114_pa_set_selection(struct v4l2_subdev *sd, struct mt9m114 *sensor = pa_to_mt9m114(sd); struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; + int ret = 0; if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; @@ -1282,25 +1308,41 @@ static int mt9m114_pa_set_selection(struct v4l2_subdev *sd, * binning, but binning is configured after setting the selection, so * we can't know tell here if it will be used. */ - crop->left = ALIGN(sel->r.left, 4); - crop->top = ALIGN(sel->r.top, 2); - crop->width = clamp_t(unsigned int, ALIGN(sel->r.width, 4), - MT9M114_PIXEL_ARRAY_MIN_OUTPUT_WIDTH, - MT9M114_PIXEL_ARRAY_WIDTH - crop->left); - crop->height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), - MT9M114_PIXEL_ARRAY_MIN_OUTPUT_HEIGHT, - MT9M114_PIXEL_ARRAY_HEIGHT - crop->top); - - sel->r = *crop; + sel->r.left = ALIGN(sel->r.left, 4); + sel->r.top = ALIGN(sel->r.top, 2); + sel->r.width = clamp_t(unsigned int, ALIGN(sel->r.width, 4), + MT9M114_PIXEL_ARRAY_MIN_OUTPUT_WIDTH, + MT9M114_PIXEL_ARRAY_WIDTH - sel->r.left); + sel->r.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), + MT9M114_PIXEL_ARRAY_MIN_OUTPUT_HEIGHT, + MT9M114_PIXEL_ARRAY_HEIGHT - sel->r.top); + + /* Changing the selection size is not allowed in streaming state. */ + if (sensor->streaming && + (sel->r.height != crop->height || sel->r.width != crop->width)) + return -EBUSY; + + *crop = sel->r; /* Reset the format. */ format->width = crop->width; format->height = crop->height; - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) - mt9m114_pa_ctrl_update_blanking(sensor, format); + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return ret; - return 0; + mt9m114_pa_ctrl_update_blanking(sensor, format); + + /* Apply values immediately if streaming. */ + if (sensor->streaming) { + ret = mt9m114_configure_pa(sensor, state); + if (ret) + return ret; + /* Changing the cropping config requires a CONFIG_CHANGE. */ + ret = mt9m114_set_state(sensor, + MT9M114_SYS_STATE_ENTER_CONFIG_CHANGE); + } + return ret; } static const struct v4l2_subdev_pad_ops mt9m114_pa_pad_ops = { @@ -1545,7 +1587,6 @@ static int mt9m114_ifp_s_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_mark_last_busy(&sensor->client->dev); pm_runtime_put_autosuspend(&sensor->client->dev); return ret; @@ -1599,13 +1640,9 @@ static int mt9m114_ifp_get_frame_interval(struct v4l2_subdev *sd, if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE) return -EINVAL; - mutex_lock(sensor->ifp.hdl.lock); - ival->numerator = 1; ival->denominator = sensor->ifp.frame_rate; - mutex_unlock(sensor->ifp.hdl.lock); - return 0; } @@ -1624,8 +1661,6 @@ static int mt9m114_ifp_set_frame_interval(struct v4l2_subdev *sd, if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE) return -EINVAL; - mutex_lock(sensor->ifp.hdl.lock); - if (ival->numerator != 0 && ival->denominator != 0) sensor->ifp.frame_rate = min_t(unsigned int, ival->denominator / ival->numerator, @@ -1639,8 +1674,6 @@ static int mt9m114_ifp_set_frame_interval(struct v4l2_subdev *sd, if (sensor->streaming) ret = mt9m114_set_frame_rate(sensor); - mutex_unlock(sensor->ifp.hdl.lock); - return ret; } @@ -2235,9 +2268,22 @@ static const struct dev_pm_ops mt9m114_pm_ops = { * Probe & Remove */ +static int mt9m114_verify_link_frequency(struct mt9m114 *sensor, + unsigned int pixrate) +{ + unsigned int link_freq = sensor->bus_cfg.bus_type == V4L2_MBUS_CSI2_DPHY + ? pixrate * 8 : pixrate * 2; + + if (sensor->bus_cfg.nr_of_link_frequencies != 1 || + sensor->bus_cfg.link_frequencies[0] != link_freq) + return -EINVAL; + + return 0; +} + static int mt9m114_clk_init(struct mt9m114 *sensor) { - unsigned int link_freq; + unsigned int pixrate; /* Hardcode the PLL multiplier and dividers to default settings. */ sensor->pll.m = 32; @@ -2249,19 +2295,29 @@ static int mt9m114_clk_init(struct mt9m114 *sensor) * for 16-bit per pixel, transmitted in DDR over a single lane. For * parallel mode, the sensor ouputs one pixel in two PIXCLK cycles. */ - sensor->pixrate = clk_get_rate(sensor->clk) * sensor->pll.m - / ((sensor->pll.n + 1) * (sensor->pll.p + 1)); - link_freq = sensor->bus_cfg.bus_type == V4L2_MBUS_CSI2_DPHY - ? sensor->pixrate * 8 : sensor->pixrate * 2; + /* + * Check if EXTCLK fits the configured link frequency. Bypass the PLL + * in this case. + */ + pixrate = clk_get_rate(sensor->clk) / 2; + if (mt9m114_verify_link_frequency(sensor, pixrate) == 0) { + sensor->pixrate = pixrate; + sensor->bypass_pll = true; + return 0; + } - if (sensor->bus_cfg.nr_of_link_frequencies != 1 || - sensor->bus_cfg.link_frequencies[0] != link_freq) { - dev_err(&sensor->client->dev, "Unsupported DT link-frequencies\n"); - return -EINVAL; + /* Check if the PLL configuration fits the configured link frequency. */ + pixrate = clk_get_rate(sensor->clk) * sensor->pll.m + / ((sensor->pll.n + 1) * (sensor->pll.p + 1)); + if (mt9m114_verify_link_frequency(sensor, pixrate) == 0) { + sensor->pixrate = pixrate; + sensor->bypass_pll = false; + return 0; } - return 0; + dev_err(&sensor->client->dev, "Unsupported DT link-frequencies\n"); + return -EINVAL; } static int mt9m114_identify(struct mt9m114 *sensor) @@ -2330,6 +2386,17 @@ static int mt9m114_parse_dt(struct mt9m114 *sensor) goto error; } + sensor->pad_slew_rate = MT9M114_PAD_SLEW_DEFAULT; + device_property_read_u32(&sensor->client->dev, "slew-rate", + &sensor->pad_slew_rate); + + if (sensor->pad_slew_rate < MT9M114_PAD_SLEW_MIN || + sensor->pad_slew_rate > MT9M114_PAD_SLEW_MAX) { + dev_err(&sensor->client->dev, "Invalid slew-rate %u\n", + sensor->pad_slew_rate); + return -EINVAL; + } + return 0; error: @@ -2360,10 +2427,10 @@ static int mt9m114_probe(struct i2c_client *client) return ret; /* Acquire clocks, GPIOs and regulators. */ - sensor->clk = devm_clk_get(dev, NULL); + sensor->clk = devm_v4l2_sensor_clk_get(dev, NULL); if (IS_ERR(sensor->clk)) { - ret = PTR_ERR(sensor->clk); - dev_err_probe(dev, ret, "Failed to get clock\n"); + ret = dev_err_probe(dev, PTR_ERR(sensor->clk), + "Failed to get clock\n"); goto error_ep_free; } @@ -2437,7 +2504,6 @@ static int mt9m114_probe(struct i2c_client *client) * Decrease the PM usage count. The device will get suspended after the * autosuspend delay, turning the power off. */ - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; |