root/drivers/staging/media/soc_camera/imx074.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. to_imx074
  2. imx074_find_datafmt
  3. reg_write
  4. reg_read
  5. imx074_set_fmt
  6. imx074_get_fmt
  7. imx074_get_selection
  8. imx074_enum_mbus_code
  9. imx074_s_stream
  10. imx074_s_power
  11. imx074_g_mbus_config
  12. imx074_video_probe
  13. imx074_probe
  14. imx074_remove

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Driver for IMX074 CMOS Image Sensor from Sony
   4  *
   5  * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
   6  *
   7  * Partially inspired by the IMX074 driver from the Android / MSM tree
   8  */
   9 #include <linux/delay.h>
  10 #include <linux/i2c.h>
  11 #include <linux/v4l2-mediabus.h>
  12 #include <linux/slab.h>
  13 #include <linux/videodev2.h>
  14 #include <linux/module.h>
  15 
  16 #include <media/soc_camera.h>
  17 #include <media/v4l2-async.h>
  18 #include <media/v4l2-clk.h>
  19 #include <media/v4l2-subdev.h>
  20 
  21 /* IMX074 registers */
  22 
  23 #define MODE_SELECT                     0x0100
  24 #define IMAGE_ORIENTATION               0x0101
  25 #define GROUPED_PARAMETER_HOLD          0x0104
  26 
  27 /* Integration Time */
  28 #define COARSE_INTEGRATION_TIME_HI      0x0202
  29 #define COARSE_INTEGRATION_TIME_LO      0x0203
  30 /* Gain */
  31 #define ANALOGUE_GAIN_CODE_GLOBAL_HI    0x0204
  32 #define ANALOGUE_GAIN_CODE_GLOBAL_LO    0x0205
  33 
  34 /* PLL registers */
  35 #define PRE_PLL_CLK_DIV                 0x0305
  36 #define PLL_MULTIPLIER                  0x0307
  37 #define PLSTATIM                        0x302b
  38 #define VNDMY_ABLMGSHLMT                0x300a
  39 #define Y_OPBADDR_START_DI              0x3014
  40 /* mode setting */
  41 #define FRAME_LENGTH_LINES_HI           0x0340
  42 #define FRAME_LENGTH_LINES_LO           0x0341
  43 #define LINE_LENGTH_PCK_HI              0x0342
  44 #define LINE_LENGTH_PCK_LO              0x0343
  45 #define YADDR_START                     0x0347
  46 #define YADDR_END                       0x034b
  47 #define X_OUTPUT_SIZE_MSB               0x034c
  48 #define X_OUTPUT_SIZE_LSB               0x034d
  49 #define Y_OUTPUT_SIZE_MSB               0x034e
  50 #define Y_OUTPUT_SIZE_LSB               0x034f
  51 #define X_EVEN_INC                      0x0381
  52 #define X_ODD_INC                       0x0383
  53 #define Y_EVEN_INC                      0x0385
  54 #define Y_ODD_INC                       0x0387
  55 
  56 #define HMODEADD                        0x3001
  57 #define VMODEADD                        0x3016
  58 #define VAPPLINE_START                  0x3069
  59 #define VAPPLINE_END                    0x306b
  60 #define SHUTTER                         0x3086
  61 #define HADDAVE                         0x30e8
  62 #define LANESEL                         0x3301
  63 
  64 /* IMX074 supported geometry */
  65 #define IMX074_WIDTH                    1052
  66 #define IMX074_HEIGHT                   780
  67 
  68 /* IMX074 has only one fixed colorspace per pixelcode */
  69 struct imx074_datafmt {
  70         u32     code;
  71         enum v4l2_colorspace            colorspace;
  72 };
  73 
  74 struct imx074 {
  75         struct v4l2_subdev              subdev;
  76         const struct imx074_datafmt     *fmt;
  77         struct v4l2_clk                 *clk;
  78 };
  79 
  80 static const struct imx074_datafmt imx074_colour_fmts[] = {
  81         {MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
  82 };
  83 
  84 static struct imx074 *to_imx074(const struct i2c_client *client)
  85 {
  86         return container_of(i2c_get_clientdata(client), struct imx074, subdev);
  87 }
  88 
  89 /* Find a data format by a pixel code in an array */
  90 static const struct imx074_datafmt *imx074_find_datafmt(u32 code)
  91 {
  92         int i;
  93 
  94         for (i = 0; i < ARRAY_SIZE(imx074_colour_fmts); i++)
  95                 if (imx074_colour_fmts[i].code == code)
  96                         return imx074_colour_fmts + i;
  97 
  98         return NULL;
  99 }
 100 
 101 static int reg_write(struct i2c_client *client, const u16 addr, const u8 data)
 102 {
 103         struct i2c_adapter *adap = client->adapter;
 104         struct i2c_msg msg;
 105         unsigned char tx[3];
 106         int ret;
 107 
 108         msg.addr = client->addr;
 109         msg.buf = tx;
 110         msg.len = 3;
 111         msg.flags = 0;
 112 
 113         tx[0] = addr >> 8;
 114         tx[1] = addr & 0xff;
 115         tx[2] = data;
 116 
 117         ret = i2c_transfer(adap, &msg, 1);
 118 
 119         mdelay(2);
 120 
 121         return ret == 1 ? 0 : -EIO;
 122 }
 123 
 124 static int reg_read(struct i2c_client *client, const u16 addr)
 125 {
 126         u8 buf[2] = {addr >> 8, addr & 0xff};
 127         int ret;
 128         struct i2c_msg msgs[] = {
 129                 {
 130                         .addr  = client->addr,
 131                         .flags = 0,
 132                         .len   = 2,
 133                         .buf   = buf,
 134                 }, {
 135                         .addr  = client->addr,
 136                         .flags = I2C_M_RD,
 137                         .len   = 2,
 138                         .buf   = buf,
 139                 },
 140         };
 141 
 142         ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
 143         if (ret < 0) {
 144                 dev_warn(&client->dev, "Reading register %x from %x failed\n",
 145                          addr, client->addr);
 146                 return ret;
 147         }
 148 
 149         return buf[0] & 0xff; /* no sign-extension */
 150 }
 151 
 152 static int imx074_set_fmt(struct v4l2_subdev *sd,
 153                 struct v4l2_subdev_pad_config *cfg,
 154                 struct v4l2_subdev_format *format)
 155 {
 156         struct v4l2_mbus_framefmt *mf = &format->format;
 157         const struct imx074_datafmt *fmt = imx074_find_datafmt(mf->code);
 158         struct i2c_client *client = v4l2_get_subdevdata(sd);
 159         struct imx074 *priv = to_imx074(client);
 160 
 161         if (format->pad)
 162                 return -EINVAL;
 163 
 164         dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code);
 165 
 166         if (!fmt) {
 167                 /* MIPI CSI could have changed the format, double-check */
 168                 if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
 169                         return -EINVAL;
 170                 mf->code        = imx074_colour_fmts[0].code;
 171                 mf->colorspace  = imx074_colour_fmts[0].colorspace;
 172         }
 173 
 174         mf->width       = IMX074_WIDTH;
 175         mf->height      = IMX074_HEIGHT;
 176         mf->field       = V4L2_FIELD_NONE;
 177 
 178         if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
 179                 priv->fmt = fmt;
 180         else
 181                 cfg->try_fmt = *mf;
 182 
 183         return 0;
 184 }
 185 
 186 static int imx074_get_fmt(struct v4l2_subdev *sd,
 187                 struct v4l2_subdev_pad_config *cfg,
 188                 struct v4l2_subdev_format *format)
 189 {
 190         struct v4l2_mbus_framefmt *mf = &format->format;
 191         struct i2c_client *client = v4l2_get_subdevdata(sd);
 192         struct imx074 *priv = to_imx074(client);
 193 
 194         const struct imx074_datafmt *fmt = priv->fmt;
 195 
 196         if (format->pad)
 197                 return -EINVAL;
 198 
 199         mf->code        = fmt->code;
 200         mf->colorspace  = fmt->colorspace;
 201         mf->width       = IMX074_WIDTH;
 202         mf->height      = IMX074_HEIGHT;
 203         mf->field       = V4L2_FIELD_NONE;
 204 
 205         return 0;
 206 }
 207 
 208 static int imx074_get_selection(struct v4l2_subdev *sd,
 209                                 struct v4l2_subdev_pad_config *cfg,
 210                                 struct v4l2_subdev_selection *sel)
 211 {
 212         if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
 213                 return -EINVAL;
 214 
 215         sel->r.left = 0;
 216         sel->r.top = 0;
 217         sel->r.width = IMX074_WIDTH;
 218         sel->r.height = IMX074_HEIGHT;
 219 
 220         switch (sel->target) {
 221         case V4L2_SEL_TGT_CROP_BOUNDS:
 222         case V4L2_SEL_TGT_CROP:
 223                 return 0;
 224         default:
 225                 return -EINVAL;
 226         }
 227 }
 228 
 229 static int imx074_enum_mbus_code(struct v4l2_subdev *sd,
 230                 struct v4l2_subdev_pad_config *cfg,
 231                 struct v4l2_subdev_mbus_code_enum *code)
 232 {
 233         if (code->pad ||
 234             (unsigned int)code->index >= ARRAY_SIZE(imx074_colour_fmts))
 235                 return -EINVAL;
 236 
 237         code->code = imx074_colour_fmts[code->index].code;
 238         return 0;
 239 }
 240 
 241 static int imx074_s_stream(struct v4l2_subdev *sd, int enable)
 242 {
 243         struct i2c_client *client = v4l2_get_subdevdata(sd);
 244 
 245         /* MODE_SELECT: stream or standby */
 246         return reg_write(client, MODE_SELECT, !!enable);
 247 }
 248 
 249 static int imx074_s_power(struct v4l2_subdev *sd, int on)
 250 {
 251         struct i2c_client *client = v4l2_get_subdevdata(sd);
 252         struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
 253         struct imx074 *priv = to_imx074(client);
 254 
 255         return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
 256 }
 257 
 258 static int imx074_g_mbus_config(struct v4l2_subdev *sd,
 259                                 struct v4l2_mbus_config *cfg)
 260 {
 261         cfg->type = V4L2_MBUS_CSI2_DPHY;
 262         cfg->flags = V4L2_MBUS_CSI2_2_LANE |
 263                 V4L2_MBUS_CSI2_CHANNEL_0 |
 264                 V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
 265 
 266         return 0;
 267 }
 268 
 269 static const struct v4l2_subdev_video_ops imx074_subdev_video_ops = {
 270         .s_stream       = imx074_s_stream,
 271         .g_mbus_config  = imx074_g_mbus_config,
 272 };
 273 
 274 static const struct v4l2_subdev_core_ops imx074_subdev_core_ops = {
 275         .s_power        = imx074_s_power,
 276 };
 277 
 278 static const struct v4l2_subdev_pad_ops imx074_subdev_pad_ops = {
 279         .enum_mbus_code = imx074_enum_mbus_code,
 280         .get_selection  = imx074_get_selection,
 281         .get_fmt        = imx074_get_fmt,
 282         .set_fmt        = imx074_set_fmt,
 283 };
 284 
 285 static const struct v4l2_subdev_ops imx074_subdev_ops = {
 286         .core   = &imx074_subdev_core_ops,
 287         .video  = &imx074_subdev_video_ops,
 288         .pad    = &imx074_subdev_pad_ops,
 289 };
 290 
 291 static int imx074_video_probe(struct i2c_client *client)
 292 {
 293         struct v4l2_subdev *subdev = i2c_get_clientdata(client);
 294         int ret;
 295         u16 id;
 296 
 297         ret = imx074_s_power(subdev, 1);
 298         if (ret < 0)
 299                 return ret;
 300 
 301         /* Read sensor Model ID */
 302         ret = reg_read(client, 0);
 303         if (ret < 0)
 304                 goto done;
 305 
 306         id = ret << 8;
 307 
 308         ret = reg_read(client, 1);
 309         if (ret < 0)
 310                 goto done;
 311 
 312         id |= ret;
 313 
 314         dev_info(&client->dev, "Chip ID 0x%04x detected\n", id);
 315 
 316         if (id != 0x74) {
 317                 ret = -ENODEV;
 318                 goto done;
 319         }
 320 
 321         /* PLL Setting EXTCLK=24MHz, 22.5times */
 322         reg_write(client, PLL_MULTIPLIER, 0x2D);
 323         reg_write(client, PRE_PLL_CLK_DIV, 0x02);
 324         reg_write(client, PLSTATIM, 0x4B);
 325 
 326         /* 2-lane mode */
 327         reg_write(client, 0x3024, 0x00);
 328 
 329         reg_write(client, IMAGE_ORIENTATION, 0x00);
 330 
 331         /* select RAW mode:
 332          * 0x08+0x08 = top 8 bits
 333          * 0x0a+0x08 = compressed 8-bits
 334          * 0x0a+0x0a = 10 bits
 335          */
 336         reg_write(client, 0x0112, 0x08);
 337         reg_write(client, 0x0113, 0x08);
 338 
 339         /* Base setting for High frame mode */
 340         reg_write(client, VNDMY_ABLMGSHLMT, 0x80);
 341         reg_write(client, Y_OPBADDR_START_DI, 0x08);
 342         reg_write(client, 0x3015, 0x37);
 343         reg_write(client, 0x301C, 0x01);
 344         reg_write(client, 0x302C, 0x05);
 345         reg_write(client, 0x3031, 0x26);
 346         reg_write(client, 0x3041, 0x60);
 347         reg_write(client, 0x3051, 0x24);
 348         reg_write(client, 0x3053, 0x34);
 349         reg_write(client, 0x3057, 0xC0);
 350         reg_write(client, 0x305C, 0x09);
 351         reg_write(client, 0x305D, 0x07);
 352         reg_write(client, 0x3060, 0x30);
 353         reg_write(client, 0x3065, 0x00);
 354         reg_write(client, 0x30AA, 0x08);
 355         reg_write(client, 0x30AB, 0x1C);
 356         reg_write(client, 0x30B0, 0x32);
 357         reg_write(client, 0x30B2, 0x83);
 358         reg_write(client, 0x30D3, 0x04);
 359         reg_write(client, 0x3106, 0x78);
 360         reg_write(client, 0x310C, 0x82);
 361         reg_write(client, 0x3304, 0x05);
 362         reg_write(client, 0x3305, 0x04);
 363         reg_write(client, 0x3306, 0x11);
 364         reg_write(client, 0x3307, 0x02);
 365         reg_write(client, 0x3308, 0x0C);
 366         reg_write(client, 0x3309, 0x06);
 367         reg_write(client, 0x330A, 0x08);
 368         reg_write(client, 0x330B, 0x04);
 369         reg_write(client, 0x330C, 0x08);
 370         reg_write(client, 0x330D, 0x06);
 371         reg_write(client, 0x330E, 0x01);
 372         reg_write(client, 0x3381, 0x00);
 373 
 374         /* V : 1/2V-addition (1,3), H : 1/2H-averaging (1,3) -> Full HD */
 375         /* 1608 = 1560 + 48 (black lines) */
 376         reg_write(client, FRAME_LENGTH_LINES_HI, 0x06);
 377         reg_write(client, FRAME_LENGTH_LINES_LO, 0x48);
 378         reg_write(client, YADDR_START, 0x00);
 379         reg_write(client, YADDR_END, 0x2F);
 380         /* 0x838 == 2104 */
 381         reg_write(client, X_OUTPUT_SIZE_MSB, 0x08);
 382         reg_write(client, X_OUTPUT_SIZE_LSB, 0x38);
 383         /* 0x618 == 1560 */
 384         reg_write(client, Y_OUTPUT_SIZE_MSB, 0x06);
 385         reg_write(client, Y_OUTPUT_SIZE_LSB, 0x18);
 386         reg_write(client, X_EVEN_INC, 0x01);
 387         reg_write(client, X_ODD_INC, 0x03);
 388         reg_write(client, Y_EVEN_INC, 0x01);
 389         reg_write(client, Y_ODD_INC, 0x03);
 390         reg_write(client, HMODEADD, 0x00);
 391         reg_write(client, VMODEADD, 0x16);
 392         reg_write(client, VAPPLINE_START, 0x24);
 393         reg_write(client, VAPPLINE_END, 0x53);
 394         reg_write(client, SHUTTER, 0x00);
 395         reg_write(client, HADDAVE, 0x80);
 396 
 397         reg_write(client, LANESEL, 0x00);
 398 
 399         reg_write(client, GROUPED_PARAMETER_HOLD, 0x00);        /* off */
 400 
 401         ret = 0;
 402 
 403 done:
 404         imx074_s_power(subdev, 0);
 405         return ret;
 406 }
 407 
 408 static int imx074_probe(struct i2c_client *client,
 409                         const struct i2c_device_id *did)
 410 {
 411         struct imx074 *priv;
 412         struct i2c_adapter *adapter = client->adapter;
 413         struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
 414         int ret;
 415 
 416         if (!ssdd) {
 417                 dev_err(&client->dev, "IMX074: missing platform data!\n");
 418                 return -EINVAL;
 419         }
 420 
 421         if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
 422                 dev_warn(&adapter->dev,
 423                          "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
 424                 return -EIO;
 425         }
 426 
 427         priv = devm_kzalloc(&client->dev, sizeof(struct imx074), GFP_KERNEL);
 428         if (!priv)
 429                 return -ENOMEM;
 430 
 431         v4l2_i2c_subdev_init(&priv->subdev, client, &imx074_subdev_ops);
 432 
 433         priv->fmt       = &imx074_colour_fmts[0];
 434 
 435         priv->clk = v4l2_clk_get(&client->dev, "mclk");
 436         if (IS_ERR(priv->clk)) {
 437                 dev_info(&client->dev, "Error %ld getting clock\n", PTR_ERR(priv->clk));
 438                 return -EPROBE_DEFER;
 439         }
 440 
 441         ret = soc_camera_power_init(&client->dev, ssdd);
 442         if (ret < 0)
 443                 goto epwrinit;
 444 
 445         ret = imx074_video_probe(client);
 446         if (ret < 0)
 447                 goto eprobe;
 448 
 449         ret = v4l2_async_register_subdev(&priv->subdev);
 450         if (!ret)
 451                 return 0;
 452 
 453 epwrinit:
 454 eprobe:
 455         v4l2_clk_put(priv->clk);
 456         return ret;
 457 }
 458 
 459 static int imx074_remove(struct i2c_client *client)
 460 {
 461         struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
 462         struct imx074 *priv = to_imx074(client);
 463 
 464         v4l2_async_unregister_subdev(&priv->subdev);
 465         v4l2_clk_put(priv->clk);
 466 
 467         if (ssdd->free_bus)
 468                 ssdd->free_bus(ssdd);
 469 
 470         return 0;
 471 }
 472 
 473 static const struct i2c_device_id imx074_id[] = {
 474         { "imx074", 0 },
 475         { }
 476 };
 477 MODULE_DEVICE_TABLE(i2c, imx074_id);
 478 
 479 static struct i2c_driver imx074_i2c_driver = {
 480         .driver = {
 481                 .name = "imx074",
 482         },
 483         .probe          = imx074_probe,
 484         .remove         = imx074_remove,
 485         .id_table       = imx074_id,
 486 };
 487 
 488 module_i2c_driver(imx074_i2c_driver);
 489 
 490 MODULE_DESCRIPTION("Sony IMX074 Camera driver");
 491 MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
 492 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */