Hi Sakari,

On Friday 09 March 2012 21:15:28 Sakari Ailus wrote:
> Hi Laurent,
> 
> Thanks for the patch. Just a few minor comments below.

Thanks for the review.

> Laurent Pinchart wrote:
> ...
> 
> > +static int mt9m032_setup_pll(struct mt9m032 *sensor)
> > +{
> > +   static const struct aptina_pll_limits limits = {
> > +           .ext_clock_min = 8000000,
> > +           .ext_clock_max = 16500000,
> > +           .int_clock_min = 2000000,
> > +           .int_clock_max = 24000000,
> > +           .out_clock_min = 322000000,
> > +           .out_clock_max = 693000000,
> > +           .pix_clock_max = 99000000,
> > +           .n_min = 1,
> > +           .n_max = 64,
> > +           .m_min = 16,
> > +           .m_max = 255,
> > +           .p1_min = 1,
> > +           .p1_max = 128,
> > +   };
> > +
> > +   struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
> > +   struct mt9m032_platform_data *pdata = sensor->pdata;
> > +   struct aptina_pll pll;
> > +   int ret;
> > +
> > +   pll.ext_clock = pdata->ext_clock;
> > +   pll.pix_clock = pdata->pix_clock;
> > +
> > +   ret = aptina_pll_calculate(&client->dev, &limits, &pll);
> > +   if (ret < 0)
> > +           return ret;
> > +
> > +   sensor->pix_clock = pll.pix_clock;
> 
> I wouldn't expect aptina_pll_calculate() to change the supplied pixel
> clock. I'd consider it a bug if it does that. So you could use the pixel
> clock from platform data equally well.

But does it make a difference ? :-) Taking the value from pll.pix_clock seems 
more logical to me.

> > +   ret = mt9m032_write(client, MT9M032_PLL_CONFIG1,
> > +                       (pll.m << MT9M032_PLL_CONFIG1_MUL_SHIFT)
> > +                       | (pll.p1 - 1));
> > +   if (!ret)
> > +           ret = mt9m032_write(client, MT9P031_PLL_CONFIG2, pll.n - 1);
> > +   if (!ret)
> > +           ret = mt9m032_write(client, MT9P031_PLL_CONTROL,
> > +                               MT9P031_PLL_CONTROL_PWRON |
> > +                               MT9P031_PLL_CONTROL_USEPLL);
> > +   if (!ret)               /* more reserved, Continuous, Master Mode */
> > +           ret = mt9m032_write(client, MT9M032_READ_MODE1, 0x8006);
> > +   if (!ret)               /* Set 14-bit mode, select 7 divider */
> > +           ret = mt9m032_write(client, MT9M032_FORMATTER1, 0x111e);
> > +
> > +   return ret;
> > +}
> 
> ...
> 
> > +static int mt9m032_set_pad_format(struct v4l2_subdev *subdev,
> > +                             struct v4l2_subdev_fh *fh,
> > +                             struct v4l2_subdev_format *fmt)
> > +{
> > +   struct mt9m032 *sensor = to_mt9m032(subdev);
> > +   int ret;
> > +
> > +   mutex_lock(&sensor->lock);
> > +
> > +   if (sensor->streaming && fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
> > +           ret = -EBUSY;
> > +           goto done;
> > +   }
> > +
> > +   /* Scaling is not supported, the format is thus fixed. */
> > +   ret = mt9m032_get_pad_format(subdev, fh, fmt);
> > +
> > +done:
> > +   mutex_lock(&sensor->lock);
> > +   return ret;
> > +}
> > +
> > +static int mt9m032_get_crop(struct v4l2_subdev *subdev,
> > +                       struct v4l2_subdev_fh *fh,
> > +                       struct v4l2_subdev_crop *crop)
> > +{
> > +   struct mt9m032 *sensor = to_mt9m032(subdev);
> > +
> > +   mutex_lock(&sensor->lock);
> > +   crop->rect = *__mt9m032_get_pad_crop(sensor, fh, crop->which);
> > +   mutex_unlock(&sensor->lock);
> > +
> > +   return 0;
> > +}
> 
> Shouldn't these two be renamed --- you've got "pad" in set/get fmt names
> as well.

Done.

> > +static int mt9m032_set_crop(struct v4l2_subdev *subdev,
> > +                       struct v4l2_subdev_fh *fh,
> > +                       struct v4l2_subdev_crop *crop)
> > +{
> > +   struct mt9m032 *sensor = to_mt9m032(subdev);
> > +   struct v4l2_mbus_framefmt *format;
> > +   struct v4l2_rect *__crop;
> > +   struct v4l2_rect rect;
> > +   int ret = 0;
> > +
> > +   mutex_lock(&sensor->lock);
> > +
> > +   if (sensor->streaming && crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
> > +           ret = -EBUSY;
> > +           goto done;
> > +   }
> > +
> > +   /* 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), 
MT9M032_COLUMN_START_MIN,
> > +                     MT9M032_COLUMN_START_MAX);
> > +   rect.top = clamp(ALIGN(crop->rect.top, 2), MT9M032_ROW_START_MIN,
> > +                    MT9M032_ROW_START_MAX);
> > +   rect.width = clamp(ALIGN(crop->rect.width, 2), 
MT9M032_COLUMN_SIZE_MIN,
> > +                      MT9M032_COLUMN_SIZE_MAX);
> > +   rect.height = clamp(ALIGN(crop->rect.height, 2), 
MT9M032_ROW_SIZE_MIN,
> > +                       MT9M032_ROW_SIZE_MAX);
> > +
> > +   rect.width = min(rect.width, MT9M032_PIXEL_ARRAY_WIDTH - rect.left);
> > +   rect.height = min(rect.height, MT9M032_PIXEL_ARRAY_HEIGHT - 
rect.top);
> > +
> > +   __crop = __mt9m032_get_pad_crop(sensor, fh, crop->which);
> > +
> > +   if (rect.width != __crop->width || rect.height != __crop->height) {
> > +           /* Reset the output image size if the crop rectangle size has
> > +            * been modified.
> > +            */
> > +           format = __mt9m032_get_pad_format(sensor, fh, crop->which);
> > +           format->width = rect.width;
> > +           format->height = rect.height;
> > +   }
> > +
> > +   *__crop = rect;
> > +   crop->rect = rect;
> > +
> > +   if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE)
> > +           ret = mt9m032_update_geom_timing(sensor);
> > +
> > +done:
> > +   mutex_unlock(&sensor->lock);
> > +   return ret;
> > +}
> 
> ...
> 
> > +static int mt9m032_probe(struct i2c_client *client,
> > +                    const struct i2c_device_id *devid)
> > +{
> > +   struct i2c_adapter *adapter = client->adapter;
> > +   struct mt9m032 *sensor;
> > +   int chip_version;
> > +   int ret;
> > +
> > +   if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
> > +           dev_warn(&client->dev,
> > +                    "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
> > +           return -EIO;
> > +   }
> > +
> > +   if (!client->dev.platform_data)
> > +           return -ENODEV;
> > +
> > +   sensor = kzalloc(sizeof(*sensor), GFP_KERNEL);
> > +   if (sensor == NULL)
> > +           return -ENOMEM;
> > +
> > +   mutex_init(&sensor->lock);
> > +
> > +   sensor->pdata = client->dev.platform_data;
> > +
> > +   v4l2_i2c_subdev_init(&sensor->subdev, client, &mt9m032_ops);
> > +   sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> > +
> > +   chip_version = mt9m032_read(client, MT9M032_CHIP_VERSION);
> > +   if (chip_version != MT9M032_CHIP_VERSION_VALUE) {
> > +           dev_err(&client->dev, "MT9M032 not detected, wrong version "
> > +                   "0x%04x\n", chip_version);
> > +           ret = -ENODEV;
> > +           goto free_sensor;
> > +   }
> > +
> > +   dev_info(&client->dev, "MT9M032 detected at address 0x%02x\n",
> > +            client->addr);
> > +
> > +   sensor->frame_interval.numerator = 1;
> > +   sensor->frame_interval.denominator = 30;
> > +
> > +   sensor->crop.left = 416;
> > +   sensor->crop.top = 360;
> > +   sensor->crop.width = 640;
> > +   sensor->crop.height = 480;
> 
> How was the situation with this? Shouldn't the default be no cropping?

Oops. I need to sleep as well.

Fixed.

> > +   sensor->format.width = sensor->crop.width;
> > +   sensor->format.height = sensor->crop.height;
> > +   sensor->format.code = V4L2_MBUS_FMT_Y8_1X8;
> > +   sensor->format.field = V4L2_FIELD_NONE;
> > +   sensor->format.colorspace = V4L2_COLORSPACE_SRGB;
> > +
> > +   v4l2_ctrl_handler_init(&sensor->ctrls, 4);
> > +
> > +   v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops,
> > +                     V4L2_CID_GAIN, 0, 127, 1, 64);
> > +
> > +   sensor->hflip = v4l2_ctrl_new_std(&sensor->ctrls,
> > +                                     &mt9m032_ctrl_ops,
> > +                                     V4L2_CID_HFLIP, 0, 1, 1, 0);
> > +   sensor->vflip = v4l2_ctrl_new_std(&sensor->ctrls,
> > +                                     &mt9m032_ctrl_ops,
> > +                                     V4L2_CID_VFLIP, 0, 1, 1, 0);
> > +
> > +   v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops,
> > +                     V4L2_CID_EXPOSURE, MT9M032_SHUTTER_WIDTH_MIN,
> > +                     MT9M032_SHUTTER_WIDTH_MAX, 1,
> > +                     MT9M032_SHUTTER_WIDTH_DEF);
> > +
> > +   if (sensor->ctrls.error) {
> > +           ret = sensor->ctrls.error;
> > +           dev_err(&client->dev, "control initialization error %d\n", ret);
> > +           goto free_ctrl;
> > +   }
> > +
> > +   v4l2_ctrl_cluster(2, &sensor->hflip);
> > +
> > +   sensor->subdev.ctrl_handler = &sensor->ctrls;
> > +   sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
> > +   ret = media_entity_init(&sensor->subdev.entity, 1, &sensor->pad, 0);
> > +   if (ret < 0)
> > +           goto free_ctrl;
> > +
> > +   ret = mt9m032_write(client, MT9M032_RESET, 1);  /* reset on */
> > +   if (ret < 0)
> > +           goto free_ctrl;
> 
> I think you should do media_entity_cleanup() if this or anything past
> this fails.

Good point. Fixed.

> > +   mt9m032_write(client, MT9M032_RESET, 0);        /* reset off */
> > +   if (ret < 0)
> > +           goto free_ctrl;
> > +
> > +   ret = mt9m032_setup_pll(sensor);
> > +   if (ret < 0)
> > +           goto free_ctrl;
> > +   usleep_range(10000, 11000);
> > +
> > +   v4l2_ctrl_handler_setup(&sensor->ctrls);
> > +
> > +   /* SIZE */
> > +   ret = mt9m032_update_geom_timing(sensor);
> > +   if (ret < 0)
> > +           goto free_ctrl;
> > +
> > +   ret = mt9m032_write(client, 0x41, 0x0000);      /* reserved !!! */
> > +   if (ret < 0)
> > +           goto free_ctrl;
> > +   ret = mt9m032_write(client, 0x42, 0x0003);      /* reserved !!! */
> > +   if (ret < 0)
> > +           goto free_ctrl;
> > +   ret = mt9m032_write(client, 0x43, 0x0003);      /* reserved !!! */
> > +   if (ret < 0)
> > +           goto free_ctrl;
> > +   ret = mt9m032_write(client, 0x7f, 0x0000);      /* reserved !!! */
> > +   if (ret < 0)
> > +           goto free_ctrl;
> > +   if (sensor->pdata->invert_pixclock) {
> > +           ret = mt9m032_write(client, MT9M032_PIX_CLK_CTRL,
> > +                               MT9M032_PIX_CLK_CTRL_INV_PIXCLK);
> > +           if (ret < 0)
> > +                   goto free_ctrl;
> > +   }
> > +
> > +   ret = mt9m032_write(client, MT9M032_RESTART, 1); /* Restart on */
> > +   if (ret < 0)
> > +           goto free_ctrl;
> > +   msleep(100);
> > +   ret = mt9m032_write(client, MT9M032_RESTART, 0); /* Restart off */
> > +   if (ret < 0)
> > +           goto free_ctrl;
> > +   msleep(100);
> > +   ret = update_formatter2(sensor, false);
> > +   if (ret < 0)
> > +           goto free_ctrl;
> > +
> > +   return ret;
> > +
> > +free_ctrl:
> > +   v4l2_ctrl_handler_free(&sensor->ctrls);
> > +
> > +free_sensor:
> > +   mutex_destroy(&sensor->lock);
> > +   kfree(sensor);
> > +   return ret;
> > +}
> > +
> > +static int mt9m032_remove(struct i2c_client *client)
> > +{
> > +   struct v4l2_subdev *subdev = i2c_get_clientdata(client);
> > +   struct mt9m032 *sensor = to_mt9m032(subdev);
> > +
> > +   v4l2_device_unregister_subdev(&sensor->subdev);
> > +   v4l2_ctrl_handler_free(&sensor->ctrls);
> > +   media_entity_cleanup(&sensor->subdev.entity);
> > +   mutex_destroy(&sensor->lock);
> > +   kfree(sensor);
> > +   return 0;
> > +}

-- 
Regards,

Laurent Pinchart

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to