--- zzzz-none-000/linux-3.10.107/drivers/media/i2c/mt9p031.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/media/i2c/mt9p031.c 2021-02-04 17:41:59.000000000 +0000 @@ -15,17 +15,19 @@ #include #include #include -#include -#include +#include #include #include +#include +#include +#include #include #include #include #include #include -#include +#include #include #include #include @@ -76,6 +78,9 @@ #define MT9P031_PLL_CONFIG_1 0x11 #define MT9P031_PLL_CONFIG_2 0x12 #define MT9P031_PIXEL_CLOCK_CONTROL 0x0a +#define MT9P031_PIXEL_CLOCK_INVERT (1 << 15) +#define MT9P031_PIXEL_CLOCK_SHIFT(n) ((n) << 8) +#define MT9P031_PIXEL_CLOCK_DIVIDE(n) ((n) << 0) #define MT9P031_FRAME_RESTART 0x0b #define MT9P031_SHUTTER_DELAY 0x0c #define MT9P031_RST 0x0d @@ -124,13 +129,13 @@ int power_count; struct clk *clk; - struct regulator *vaa; - struct regulator *vdd; - struct regulator *vdd_io; + struct regulator_bulk_data regulators[3]; enum mt9p031_model model; struct aptina_pll pll; - int reset; + unsigned int clk_div; + bool use_pll; + struct gpio_desc *reset; struct v4l2_ctrl_handler ctrls; struct v4l2_ctrl *blc_auto; @@ -198,6 +203,11 @@ if (ret < 0) return ret; + ret = mt9p031_write(client, MT9P031_PIXEL_CLOCK_CONTROL, + MT9P031_PIXEL_CLOCK_DIVIDE(mt9p031->clk_div)); + if (ret < 0) + return ret; + return mt9p031_set_output_control(mt9p031, MT9P031_OUTPUT_CONTROL_CEN, 0); } @@ -222,15 +232,34 @@ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); struct mt9p031_platform_data *pdata = mt9p031->pdata; + int ret; mt9p031->clk = devm_clk_get(&client->dev, NULL); if (IS_ERR(mt9p031->clk)) return PTR_ERR(mt9p031->clk); - clk_set_rate(mt9p031->clk, pdata->ext_freq); + ret = clk_set_rate(mt9p031->clk, pdata->ext_freq); + if (ret < 0) + return ret; + + /* If the external clock frequency is out of bounds for the PLL use the + * pixel clock divider only and disable the PLL. + */ + if (pdata->ext_freq > limits.ext_clock_max) { + unsigned int div; + + div = DIV_ROUND_UP(pdata->ext_freq, pdata->target_freq); + div = roundup_pow_of_two(div) / 2; + + mt9p031->clk_div = min_t(unsigned int, div, 64); + mt9p031->use_pll = false; + + return 0; + } mt9p031->pll.ext_clock = pdata->ext_freq; mt9p031->pll.pix_clock = pdata->target_freq; + mt9p031->use_pll = true; return aptina_pll_calculate(&client->dev, &limits, &mt9p031->pll); } @@ -240,6 +269,9 @@ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); int ret; + if (!mt9p031->use_pll) + return 0; + ret = mt9p031_write(client, MT9P031_PLL_CONTROL, MT9P031_PLL_CONTROL_PWRON); if (ret < 0) @@ -265,30 +297,42 @@ { struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + if (!mt9p031->use_pll) + return 0; + return mt9p031_write(client, MT9P031_PLL_CONTROL, MT9P031_PLL_CONTROL_PWROFF); } static int mt9p031_power_on(struct mt9p031 *mt9p031) { - /* Ensure RESET_BAR is low */ - if (mt9p031->reset != -1) { - gpio_set_value(mt9p031->reset, 0); + int ret; + + /* Ensure RESET_BAR is active */ + if (mt9p031->reset) { + gpiod_set_value(mt9p031->reset, 1); usleep_range(1000, 2000); } /* Bring up the supplies */ - regulator_enable(mt9p031->vdd); - regulator_enable(mt9p031->vdd_io); - regulator_enable(mt9p031->vaa); + ret = regulator_bulk_enable(ARRAY_SIZE(mt9p031->regulators), + mt9p031->regulators); + if (ret < 0) + return ret; - /* Emable clock */ - if (mt9p031->clk) - clk_prepare_enable(mt9p031->clk); + /* Enable clock */ + if (mt9p031->clk) { + ret = clk_prepare_enable(mt9p031->clk); + if (ret) { + regulator_bulk_disable(ARRAY_SIZE(mt9p031->regulators), + mt9p031->regulators); + return ret; + } + } /* Now RESET_BAR must be high */ - if (mt9p031->reset != -1) { - gpio_set_value(mt9p031->reset, 1); + if (mt9p031->reset) { + gpiod_set_value(mt9p031->reset, 0); usleep_range(1000, 2000); } @@ -297,14 +341,13 @@ static void mt9p031_power_off(struct mt9p031 *mt9p031) { - if (mt9p031->reset != -1) { - gpio_set_value(mt9p031->reset, 0); + if (mt9p031->reset) { + gpiod_set_value(mt9p031->reset, 1); usleep_range(1000, 2000); } - regulator_disable(mt9p031->vaa); - regulator_disable(mt9p031->vdd_io); - regulator_disable(mt9p031->vdd); + regulator_bulk_disable(ARRAY_SIZE(mt9p031->regulators), + mt9p031->regulators); if (mt9p031->clk) clk_disable_unprepare(mt9p031->clk); @@ -431,7 +474,7 @@ } static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { struct mt9p031 *mt9p031 = to_mt9p031(subdev); @@ -444,7 +487,7 @@ } static int mt9p031_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct mt9p031 *mt9p031 = to_mt9p031(subdev); @@ -462,12 +505,12 @@ } static struct v4l2_mbus_framefmt * -__mt9p031_get_pad_format(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh, +__mt9p031_get_pad_format(struct mt9p031 *mt9p031, struct v4l2_subdev_pad_config *cfg, unsigned int pad, u32 which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(&mt9p031->subdev, cfg, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9p031->format; default: @@ -476,12 +519,12 @@ } static struct v4l2_rect * -__mt9p031_get_pad_crop(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh, +__mt9p031_get_pad_crop(struct mt9p031 *mt9p031, struct v4l2_subdev_pad_config *cfg, unsigned int pad, u32 which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(fh, pad); + return v4l2_subdev_get_try_crop(&mt9p031->subdev, cfg, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9p031->crop; default: @@ -490,18 +533,18 @@ } static int mt9p031_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct mt9p031 *mt9p031 = to_mt9p031(subdev); - fmt->format = *__mt9p031_get_pad_format(mt9p031, fh, fmt->pad, + fmt->format = *__mt9p031_get_pad_format(mt9p031, cfg, fmt->pad, fmt->which); return 0; } static int mt9p031_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { struct mt9p031 *mt9p031 = to_mt9p031(subdev); @@ -512,21 +555,23 @@ unsigned int hratio; unsigned int vratio; - __crop = __mt9p031_get_pad_crop(mt9p031, fh, format->pad, + __crop = __mt9p031_get_pad_crop(mt9p031, cfg, format->pad, format->which); /* Clamp the width and height to avoid dividing by zero. */ width = clamp_t(unsigned int, ALIGN(format->format.width, 2), - max(__crop->width / 7, MT9P031_WINDOW_WIDTH_MIN), + max_t(unsigned int, __crop->width / 7, + MT9P031_WINDOW_WIDTH_MIN), __crop->width); height = clamp_t(unsigned int, ALIGN(format->format.height, 2), - max(__crop->height / 8, MT9P031_WINDOW_HEIGHT_MIN), - __crop->height); + max_t(unsigned int, __crop->height / 8, + MT9P031_WINDOW_HEIGHT_MIN), + __crop->height); hratio = DIV_ROUND_CLOSEST(__crop->width, width); vratio = DIV_ROUND_CLOSEST(__crop->height, height); - __format = __mt9p031_get_pad_format(mt9p031, fh, format->pad, + __format = __mt9p031_get_pad_format(mt9p031, cfg, format->pad, format->which); __format->width = __crop->width / hratio; __format->height = __crop->height / vratio; @@ -536,57 +581,64 @@ return 0; } -static int mt9p031_get_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int mt9p031_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { struct mt9p031 *mt9p031 = to_mt9p031(subdev); - crop->rect = *__mt9p031_get_pad_crop(mt9p031, fh, crop->pad, - crop->which); + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + sel->r = *__mt9p031_get_pad_crop(mt9p031, cfg, sel->pad, sel->which); return 0; } -static int mt9p031_set_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int mt9p031_set_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { struct mt9p031 *mt9p031 = to_mt9p031(subdev); struct v4l2_mbus_framefmt *__format; struct v4l2_rect *__crop; struct v4l2_rect rect; + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + /* Clamp the crop rectangle boundaries and align them to a multiple of 2 * pixels to ensure a GRBG Bayer pattern. */ - rect.left = clamp(ALIGN(crop->rect.left, 2), MT9P031_COLUMN_START_MIN, + rect.left = clamp(ALIGN(sel->r.left, 2), MT9P031_COLUMN_START_MIN, MT9P031_COLUMN_START_MAX); - rect.top = clamp(ALIGN(crop->rect.top, 2), MT9P031_ROW_START_MIN, + rect.top = clamp(ALIGN(sel->r.top, 2), MT9P031_ROW_START_MIN, MT9P031_ROW_START_MAX); - rect.width = clamp(ALIGN(crop->rect.width, 2), - MT9P031_WINDOW_WIDTH_MIN, - MT9P031_WINDOW_WIDTH_MAX); - rect.height = clamp(ALIGN(crop->rect.height, 2), - MT9P031_WINDOW_HEIGHT_MIN, - MT9P031_WINDOW_HEIGHT_MAX); - - rect.width = min(rect.width, MT9P031_PIXEL_ARRAY_WIDTH - rect.left); - rect.height = min(rect.height, MT9P031_PIXEL_ARRAY_HEIGHT - rect.top); + rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2), + MT9P031_WINDOW_WIDTH_MIN, + MT9P031_WINDOW_WIDTH_MAX); + rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), + MT9P031_WINDOW_HEIGHT_MIN, + MT9P031_WINDOW_HEIGHT_MAX); + + rect.width = min_t(unsigned int, rect.width, + MT9P031_PIXEL_ARRAY_WIDTH - rect.left); + rect.height = min_t(unsigned int, rect.height, + MT9P031_PIXEL_ARRAY_HEIGHT - rect.top); - __crop = __mt9p031_get_pad_crop(mt9p031, fh, crop->pad, crop->which); + __crop = __mt9p031_get_pad_crop(mt9p031, cfg, sel->pad, sel->which); if (rect.width != __crop->width || rect.height != __crop->height) { /* Reset the output image size if the crop rectangle size has * been modified. */ - __format = __mt9p031_get_pad_format(mt9p031, fh, crop->pad, - crop->which); + __format = __mt9p031_get_pad_format(mt9p031, cfg, sel->pad, + sel->which); __format->width = rect.width; __format->height = rect.height; } *__crop = rect; - crop->rect = rect; + sel->r = rect; return 0; } @@ -600,6 +652,28 @@ #define V4L2_CID_BLC_ANALOG_OFFSET (V4L2_CID_USER_BASE | 0x1004) #define V4L2_CID_BLC_DIGITAL_OFFSET (V4L2_CID_USER_BASE | 0x1005) +static int mt9p031_restore_blc(struct mt9p031 *mt9p031) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + int ret; + + if (mt9p031->blc_auto->cur.val != 0) { + ret = mt9p031_set_mode2(mt9p031, 0, + MT9P031_READ_MODE_2_ROW_BLC); + if (ret < 0) + return ret; + } + + if (mt9p031->blc_offset->cur.val != 0) { + ret = mt9p031_write(client, MT9P031_ROW_BLACK_TARGET, + mt9p031->blc_offset->cur.val); + if (ret < 0) + return ret; + } + + return 0; +} + static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) { struct mt9p031 *mt9p031 = @@ -608,6 +682,9 @@ u16 data; int ret; + if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) + return 0; + switch (ctrl->id) { case V4L2_CID_EXPOSURE: ret = mt9p031_write(client, MT9P031_SHUTTER_WIDTH_UPPER, @@ -662,18 +739,20 @@ MT9P031_READ_MODE_2_ROW_MIR, 0); case V4L2_CID_TEST_PATTERN: + /* The digital side of the Black Level Calibration function must + * be disabled when generating a test pattern to avoid artifacts + * in the image. Activate (deactivate) the BLC-related controls + * when the test pattern is enabled (disabled). + */ + v4l2_ctrl_activate(mt9p031->blc_auto, ctrl->val == 0); + v4l2_ctrl_activate(mt9p031->blc_offset, ctrl->val == 0); + if (!ctrl->val) { - /* Restore the black level compensation settings. */ - if (mt9p031->blc_auto->cur.val != 0) { - ret = mt9p031_s_ctrl(mt9p031->blc_auto); - if (ret < 0) - return ret; - } - if (mt9p031->blc_offset->cur.val != 0) { - ret = mt9p031_s_ctrl(mt9p031->blc_offset); - if (ret < 0) - return ret; - } + /* Restore the BLC settings. */ + ret = mt9p031_restore_blc(mt9p031); + if (ret < 0) + return ret; + return mt9p031_write(client, MT9P031_TEST_PATTERN, MT9P031_TEST_PATTERN_DISABLE); } @@ -688,9 +767,7 @@ if (ret < 0) return ret; - /* Disable digital black level compensation when using a test - * pattern. - */ + /* Disable digital BLC when generating a test pattern. */ ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC, 0); if (ret < 0) @@ -849,18 +926,18 @@ /* Read out the chip version register */ data = mt9p031_read(client, MT9P031_CHIP_VERSION); + mt9p031_power_off(mt9p031); + if (data != MT9P031_CHIP_VERSION_VALUE) { dev_err(&client->dev, "MT9P031 not detected, wrong version " "0x%04x\n", data); return -ENODEV; } - mt9p031_power_off(mt9p031); - dev_info(&client->dev, "MT9P031 detected at address 0x%02x\n", client->addr); - return ret; + return 0; } static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) @@ -869,18 +946,18 @@ struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - crop = v4l2_subdev_get_try_crop(fh, 0); + crop = v4l2_subdev_get_try_crop(subdev, fh->pad, 0); crop->left = MT9P031_COLUMN_START_DEF; crop->top = MT9P031_ROW_START_DEF; crop->width = MT9P031_WINDOW_WIDTH_DEF; crop->height = MT9P031_WINDOW_HEIGHT_DEF; - format = v4l2_subdev_get_try_format(fh, 0); + format = v4l2_subdev_get_try_format(subdev, fh->pad, 0); if (mt9p031->model == MT9P031_MODEL_MONOCHROME) - format->code = V4L2_MBUS_FMT_Y12_1X12; + format->code = MEDIA_BUS_FMT_Y12_1X12; else - format->code = V4L2_MBUS_FMT_SGRBG12_1X12; + format->code = MEDIA_BUS_FMT_SGRBG12_1X12; format->width = MT9P031_WINDOW_WIDTH_DEF; format->height = MT9P031_WINDOW_HEIGHT_DEF; @@ -908,8 +985,8 @@ .enum_frame_size = mt9p031_enum_frame_size, .get_fmt = mt9p031_get_format, .set_fmt = mt9p031_set_format, - .get_crop = mt9p031_get_crop, - .set_crop = mt9p031_set_crop, + .get_selection = mt9p031_get_selection, + .set_selection = mt9p031_set_selection, }; static struct v4l2_subdev_ops mt9p031_subdev_ops = { @@ -928,10 +1005,35 @@ * Driver initialization and probing */ +static struct mt9p031_platform_data * +mt9p031_get_pdata(struct i2c_client *client) +{ + struct mt9p031_platform_data *pdata; + struct device_node *np; + + if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node) + return client->dev.platform_data; + + np = of_graph_get_next_endpoint(client->dev.of_node, NULL); + if (!np) + return NULL; + + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + goto done; + + of_property_read_u32(np, "input-clock-frequency", &pdata->ext_freq); + of_property_read_u32(np, "pixel-clock-frequency", &pdata->target_freq); + +done: + of_node_put(np); + return pdata; +} + static int mt9p031_probe(struct i2c_client *client, const struct i2c_device_id *did) { - struct mt9p031_platform_data *pdata = client->dev.platform_data; + struct mt9p031_platform_data *pdata = mt9p031_get_pdata(client); struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); struct mt9p031 *mt9p031; unsigned int i; @@ -956,18 +1058,19 @@ mt9p031->output_control = MT9P031_OUTPUT_CONTROL_DEF; mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC; mt9p031->model = did->driver_data; - mt9p031->reset = -1; - mt9p031->vaa = devm_regulator_get(&client->dev, "vaa"); - mt9p031->vdd = devm_regulator_get(&client->dev, "vdd"); - mt9p031->vdd_io = devm_regulator_get(&client->dev, "vdd_io"); + mt9p031->regulators[0].supply = "vdd"; + mt9p031->regulators[1].supply = "vdd_io"; + mt9p031->regulators[2].supply = "vaa"; - if (IS_ERR(mt9p031->vaa) || IS_ERR(mt9p031->vdd) || - IS_ERR(mt9p031->vdd_io)) { + ret = devm_regulator_bulk_get(&client->dev, 3, mt9p031->regulators); + if (ret < 0) { dev_err(&client->dev, "Unable to get regulators\n"); - return -ENODEV; + return ret; } + mutex_init(&mt9p031->power_lock); + v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 6); v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, @@ -1005,7 +1108,6 @@ mt9p031->blc_offset = v4l2_ctrl_find(&mt9p031->ctrls, V4L2_CID_BLC_DIGITAL_OFFSET); - mutex_init(&mt9p031->power_lock); v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops); mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops; @@ -1022,30 +1124,29 @@ mt9p031->crop.top = MT9P031_ROW_START_DEF; if (mt9p031->model == MT9P031_MODEL_MONOCHROME) - mt9p031->format.code = V4L2_MBUS_FMT_Y12_1X12; + mt9p031->format.code = MEDIA_BUS_FMT_Y12_1X12; else - mt9p031->format.code = V4L2_MBUS_FMT_SGRBG12_1X12; + mt9p031->format.code = MEDIA_BUS_FMT_SGRBG12_1X12; mt9p031->format.width = MT9P031_WINDOW_WIDTH_DEF; mt9p031->format.height = MT9P031_WINDOW_HEIGHT_DEF; mt9p031->format.field = V4L2_FIELD_NONE; mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB; - if (pdata->reset != -1) { - ret = devm_gpio_request_one(&client->dev, pdata->reset, - GPIOF_OUT_INIT_LOW, "mt9p031_rst"); - if (ret < 0) - goto done; - - mt9p031->reset = pdata->reset; - } + mt9p031->reset = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_HIGH); ret = mt9p031_clk_setup(mt9p031); + if (ret) + goto done; + + ret = v4l2_async_register_subdev(&mt9p031->subdev); done: if (ret < 0) { v4l2_ctrl_handler_free(&mt9p031->ctrls); media_entity_cleanup(&mt9p031->subdev.entity); + mutex_destroy(&mt9p031->power_lock); } return ret; @@ -1057,8 +1158,9 @@ struct mt9p031 *mt9p031 = to_mt9p031(subdev); v4l2_ctrl_handler_free(&mt9p031->ctrls); - v4l2_device_unregister_subdev(subdev); + v4l2_async_unregister_subdev(subdev); media_entity_cleanup(&subdev->entity); + mutex_destroy(&mt9p031->power_lock); return 0; } @@ -1070,8 +1172,18 @@ }; MODULE_DEVICE_TABLE(i2c, mt9p031_id); +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id mt9p031_of_match[] = { + { .compatible = "aptina,mt9p031", }, + { .compatible = "aptina,mt9p031m", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, mt9p031_of_match); +#endif + static struct i2c_driver mt9p031_i2c_driver = { .driver = { + .of_match_table = of_match_ptr(mt9p031_of_match), .name = "mt9p031", }, .probe = mt9p031_probe,