1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Driver for SiliconFile NOON010PC30 CIF (1/11") Image Sensor with ISP 4 * 5 * Copyright (C) 2010 - 2011 Samsung Electronics Co., Ltd. 6 * Contact: Sylwester Nawrocki, <s.nawrocki@samsung.com> 7 * 8 * Initial register configuration based on a driver authored by 9 * HeungJun Kim <riverful.kim@samsung.com>. 10 */ 11 12#include <linux/delay.h> 13#include <linux/gpio.h> 14#include <linux/i2c.h> 15#include <linux/slab.h> 16#include <linux/regulator/consumer.h> 17#include <media/i2c/noon010pc30.h> 18#include <linux/videodev2.h> 19#include <linux/module.h> 20#include <media/v4l2-ctrls.h> 21#include <media/v4l2-device.h> 22#include <media/v4l2-mediabus.h> 23#include <media/v4l2-subdev.h> 24 25static int debug; 26module_param(debug, int, 0644); 27MODULE_PARM_DESC(debug, "Enable module debug trace. Set to 1 to enable."); 28 29#define MODULE_NAME "NOON010PC30" 30 31/* 32 * Register offsets within a page 33 * b15..b8 - page id, b7..b0 - register address 34 */ 35#define POWER_CTRL_REG 0x0001 36#define PAGEMODE_REG 0x03 37#define DEVICE_ID_REG 0x0004 38#define NOON010PC30_ID 0x86 39#define VDO_CTL_REG(n) (0x0010 + (n)) 40#define SYNC_CTL_REG 0x0012 41/* Window size and position */ 42#define WIN_ROWH_REG 0x0013 43#define WIN_ROWL_REG 0x0014 44#define WIN_COLH_REG 0x0015 45#define WIN_COLL_REG 0x0016 46#define WIN_HEIGHTH_REG 0x0017 47#define WIN_HEIGHTL_REG 0x0018 48#define WIN_WIDTHH_REG 0x0019 49#define WIN_WIDTHL_REG 0x001A 50#define HBLANKH_REG 0x001B 51#define HBLANKL_REG 0x001C 52#define VSYNCH_REG 0x001D 53#define VSYNCL_REG 0x001E 54/* VSYNC control */ 55#define VS_CTL_REG(n) (0x00A1 + (n)) 56/* page 1 */ 57#define ISP_CTL_REG(n) (0x0110 + (n)) 58#define YOFS_REG 0x0119 59#define DARK_YOFS_REG 0x011A 60#define SAT_CTL_REG 0x0120 61#define BSAT_REG 0x0121 62#define RSAT_REG 0x0122 63/* Color correction */ 64#define CMC_CTL_REG 0x0130 65#define CMC_OFSGH_REG 0x0133 66#define CMC_OFSGL_REG 0x0135 67#define CMC_SIGN_REG 0x0136 68#define CMC_GOFS_REG 0x0137 69#define CMC_COEF_REG(n) (0x0138 + (n)) 70#define CMC_OFS_REG(n) (0x0141 + (n)) 71/* Gamma correction */ 72#define GMA_CTL_REG 0x0160 73#define GMA_COEF_REG(n) (0x0161 + (n)) 74/* Lens Shading */ 75#define LENS_CTRL_REG 0x01D0 76#define LENS_XCEN_REG 0x01D1 77#define LENS_YCEN_REG 0x01D2 78#define LENS_RC_REG 0x01D3 79#define LENS_GC_REG 0x01D4 80#define LENS_BC_REG 0x01D5 81#define L_AGON_REG 0x01D6 82#define L_AGOFF_REG 0x01D7 83/* Page 3 - Auto Exposure */ 84#define AE_CTL_REG(n) (0x0310 + (n)) 85#define AE_CTL9_REG 0x032C 86#define AE_CTL10_REG 0x032D 87#define AE_YLVL_REG 0x031C 88#define AE_YTH_REG(n) (0x031D + (n)) 89#define AE_WGT_REG 0x0326 90#define EXP_TIMEH_REG 0x0333 91#define EXP_TIMEM_REG 0x0334 92#define EXP_TIMEL_REG 0x0335 93#define EXP_MMINH_REG 0x0336 94#define EXP_MMINL_REG 0x0337 95#define EXP_MMAXH_REG 0x0338 96#define EXP_MMAXM_REG 0x0339 97#define EXP_MMAXL_REG 0x033A 98/* Page 4 - Auto White Balance */ 99#define AWB_CTL_REG(n) (0x0410 + (n)) 100#define AWB_ENABE 0x80 101#define AWB_WGHT_REG 0x0419 102#define BGAIN_PAR_REG(n) (0x044F + (n)) 103/* Manual white balance, when AWB_CTL2[0]=1 */ 104#define MWB_RGAIN_REG 0x0466 105#define MWB_BGAIN_REG 0x0467 106 107/* The token to mark an array end */ 108#define REG_TERM 0xFFFF 109 110struct noon010_format { 111 u32 code; 112 enum v4l2_colorspace colorspace; 113 u16 ispctl1_reg; 114}; 115 116struct noon010_frmsize { 117 u16 width; 118 u16 height; 119 int vid_ctl1; 120}; 121 122static const char * const noon010_supply_name[] = { 123 "vdd_core", "vddio", "vdda" 124}; 125 126#define NOON010_NUM_SUPPLIES ARRAY_SIZE(noon010_supply_name) 127 128struct noon010_info { 129 struct v4l2_subdev sd; 130 struct media_pad pad; 131 struct v4l2_ctrl_handler hdl; 132 struct regulator_bulk_data supply[NOON010_NUM_SUPPLIES]; 133 u32 gpio_nreset; 134 u32 gpio_nstby; 135 136 /* Protects the struct members below */ 137 struct mutex lock; 138 139 const struct noon010_format *curr_fmt; 140 const struct noon010_frmsize *curr_win; 141 unsigned int apply_new_cfg:1; 142 unsigned int streaming:1; 143 unsigned int hflip:1; 144 unsigned int vflip:1; 145 unsigned int power:1; 146 u8 i2c_reg_page; 147}; 148 149struct i2c_regval { 150 u16 addr; 151 u16 val; 152}; 153 154/* Supported resolutions. */ 155static const struct noon010_frmsize noon010_sizes[] = { 156 { 157 .width = 352, 158 .height = 288, 159 .vid_ctl1 = 0, 160 }, { 161 .width = 176, 162 .height = 144, 163 .vid_ctl1 = 0x10, 164 }, { 165 .width = 88, 166 .height = 72, 167 .vid_ctl1 = 0x20, 168 }, 169}; 170 171/* Supported pixel formats. */ 172static const struct noon010_format noon010_formats[] = { 173 { 174 .code = MEDIA_BUS_FMT_YUYV8_2X8, 175 .colorspace = V4L2_COLORSPACE_JPEG, 176 .ispctl1_reg = 0x03, 177 }, { 178 .code = MEDIA_BUS_FMT_YVYU8_2X8, 179 .colorspace = V4L2_COLORSPACE_JPEG, 180 .ispctl1_reg = 0x02, 181 }, { 182 .code = MEDIA_BUS_FMT_VYUY8_2X8, 183 .colorspace = V4L2_COLORSPACE_JPEG, 184 .ispctl1_reg = 0, 185 }, { 186 .code = MEDIA_BUS_FMT_UYVY8_2X8, 187 .colorspace = V4L2_COLORSPACE_JPEG, 188 .ispctl1_reg = 0x01, 189 }, { 190 .code = MEDIA_BUS_FMT_RGB565_2X8_BE, 191 .colorspace = V4L2_COLORSPACE_JPEG, 192 .ispctl1_reg = 0x40, 193 }, 194}; 195 196static const struct i2c_regval noon010_base_regs[] = { 197 { WIN_COLL_REG, 0x06 }, { HBLANKL_REG, 0x7C }, 198 /* Color corection and saturation */ 199 { ISP_CTL_REG(0), 0x30 }, { ISP_CTL_REG(2), 0x30 }, 200 { YOFS_REG, 0x80 }, { DARK_YOFS_REG, 0x04 }, 201 { SAT_CTL_REG, 0x1F }, { BSAT_REG, 0x90 }, 202 { CMC_CTL_REG, 0x0F }, { CMC_OFSGH_REG, 0x3C }, 203 { CMC_OFSGL_REG, 0x2C }, { CMC_SIGN_REG, 0x3F }, 204 { CMC_COEF_REG(0), 0x79 }, { CMC_OFS_REG(0), 0x00 }, 205 { CMC_COEF_REG(1), 0x39 }, { CMC_OFS_REG(1), 0x00 }, 206 { CMC_COEF_REG(2), 0x00 }, { CMC_OFS_REG(2), 0x00 }, 207 { CMC_COEF_REG(3), 0x11 }, { CMC_OFS_REG(3), 0x8B }, 208 { CMC_COEF_REG(4), 0x65 }, { CMC_OFS_REG(4), 0x07 }, 209 { CMC_COEF_REG(5), 0x14 }, { CMC_OFS_REG(5), 0x04 }, 210 { CMC_COEF_REG(6), 0x01 }, { CMC_OFS_REG(6), 0x9C }, 211 { CMC_COEF_REG(7), 0x33 }, { CMC_OFS_REG(7), 0x89 }, 212 { CMC_COEF_REG(8), 0x74 }, { CMC_OFS_REG(8), 0x25 }, 213 /* Automatic white balance */ 214 { AWB_CTL_REG(0), 0x78 }, { AWB_CTL_REG(1), 0x2E }, 215 { AWB_CTL_REG(2), 0x20 }, { AWB_CTL_REG(3), 0x85 }, 216 /* Auto exposure */ 217 { AE_CTL_REG(0), 0xDC }, { AE_CTL_REG(1), 0x81 }, 218 { AE_CTL_REG(2), 0x30 }, { AE_CTL_REG(3), 0xA5 }, 219 { AE_CTL_REG(4), 0x40 }, { AE_CTL_REG(5), 0x51 }, 220 { AE_CTL_REG(6), 0x33 }, { AE_CTL_REG(7), 0x7E }, 221 { AE_CTL9_REG, 0x00 }, { AE_CTL10_REG, 0x02 }, 222 { AE_YLVL_REG, 0x44 }, { AE_YTH_REG(0), 0x34 }, 223 { AE_YTH_REG(1), 0x30 }, { AE_WGT_REG, 0xD5 }, 224 /* Lens shading compensation */ 225 { LENS_CTRL_REG, 0x01 }, { LENS_XCEN_REG, 0x80 }, 226 { LENS_YCEN_REG, 0x70 }, { LENS_RC_REG, 0x53 }, 227 { LENS_GC_REG, 0x40 }, { LENS_BC_REG, 0x3E }, 228 { REG_TERM, 0 }, 229}; 230 231static inline struct noon010_info *to_noon010(struct v4l2_subdev *sd) 232{ 233 return container_of(sd, struct noon010_info, sd); 234} 235 236static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) 237{ 238 return &container_of(ctrl->handler, struct noon010_info, hdl)->sd; 239} 240 241static inline int set_i2c_page(struct noon010_info *info, 242 struct i2c_client *client, unsigned int reg) 243{ 244 u32 page = reg >> 8 & 0xFF; 245 int ret = 0; 246 247 if (info->i2c_reg_page != page && (reg & 0xFF) != 0x03) { 248 ret = i2c_smbus_write_byte_data(client, PAGEMODE_REG, page); 249 if (!ret) 250 info->i2c_reg_page = page; 251 } 252 return ret; 253} 254 255static int cam_i2c_read(struct v4l2_subdev *sd, u32 reg_addr) 256{ 257 struct i2c_client *client = v4l2_get_subdevdata(sd); 258 struct noon010_info *info = to_noon010(sd); 259 int ret = set_i2c_page(info, client, reg_addr); 260 261 if (ret) 262 return ret; 263 return i2c_smbus_read_byte_data(client, reg_addr & 0xFF); 264} 265 266static int cam_i2c_write(struct v4l2_subdev *sd, u32 reg_addr, u32 val) 267{ 268 struct i2c_client *client = v4l2_get_subdevdata(sd); 269 struct noon010_info *info = to_noon010(sd); 270 int ret = set_i2c_page(info, client, reg_addr); 271 272 if (ret) 273 return ret; 274 return i2c_smbus_write_byte_data(client, reg_addr & 0xFF, val); 275} 276 277static inline int noon010_bulk_write_reg(struct v4l2_subdev *sd, 278 const struct i2c_regval *msg) 279{ 280 while (msg->addr != REG_TERM) { 281 int ret = cam_i2c_write(sd, msg->addr, msg->val); 282 283 if (ret) 284 return ret; 285 msg++; 286 } 287 return 0; 288} 289 290/* Device reset and sleep mode control */ 291static int noon010_power_ctrl(struct v4l2_subdev *sd, bool reset, bool sleep) 292{ 293 struct noon010_info *info = to_noon010(sd); 294 u8 reg = sleep ? 0xF1 : 0xF0; 295 int ret = 0; 296 297 if (reset) { 298 ret = cam_i2c_write(sd, POWER_CTRL_REG, reg | 0x02); 299 udelay(20); 300 } 301 if (!ret) { 302 ret = cam_i2c_write(sd, POWER_CTRL_REG, reg); 303 if (reset && !ret) 304 info->i2c_reg_page = -1; 305 } 306 return ret; 307} 308 309/* Automatic white balance control */ 310static int noon010_enable_autowhitebalance(struct v4l2_subdev *sd, int on) 311{ 312 int ret; 313 314 ret = cam_i2c_write(sd, AWB_CTL_REG(1), on ? 0x2E : 0x2F); 315 if (!ret) 316 ret = cam_i2c_write(sd, AWB_CTL_REG(0), on ? 0xFB : 0x7B); 317 return ret; 318} 319 320/* Called with struct noon010_info.lock mutex held */ 321static int noon010_set_flip(struct v4l2_subdev *sd, int hflip, int vflip) 322{ 323 struct noon010_info *info = to_noon010(sd); 324 int reg, ret; 325 326 reg = cam_i2c_read(sd, VDO_CTL_REG(1)); 327 if (reg < 0) 328 return reg; 329 330 reg &= 0x7C; 331 if (hflip) 332 reg |= 0x01; 333 if (vflip) 334 reg |= 0x02; 335 336 ret = cam_i2c_write(sd, VDO_CTL_REG(1), reg | 0x80); 337 if (!ret) { 338 info->hflip = hflip; 339 info->vflip = vflip; 340 } 341 return ret; 342} 343 344/* Configure resolution and color format */ 345static int noon010_set_params(struct v4l2_subdev *sd) 346{ 347 struct noon010_info *info = to_noon010(sd); 348 349 int ret = cam_i2c_write(sd, VDO_CTL_REG(0), 350 info->curr_win->vid_ctl1); 351 if (ret) 352 return ret; 353 return cam_i2c_write(sd, ISP_CTL_REG(0), 354 info->curr_fmt->ispctl1_reg); 355} 356 357/* Find nearest matching image pixel size. */ 358static int noon010_try_frame_size(struct v4l2_mbus_framefmt *mf, 359 const struct noon010_frmsize **size) 360{ 361 unsigned int min_err = ~0; 362 int i = ARRAY_SIZE(noon010_sizes); 363 const struct noon010_frmsize *fsize = &noon010_sizes[0], 364 *match = NULL; 365 366 while (i--) { 367 int err = abs(fsize->width - mf->width) 368 + abs(fsize->height - mf->height); 369 370 if (err < min_err) { 371 min_err = err; 372 match = fsize; 373 } 374 fsize++; 375 } 376 if (match) { 377 mf->width = match->width; 378 mf->height = match->height; 379 if (size) 380 *size = match; 381 return 0; 382 } 383 return -EINVAL; 384} 385 386/* Called with info.lock mutex held */ 387static int power_enable(struct noon010_info *info) 388{ 389 int ret; 390 391 if (info->power) { 392 v4l2_info(&info->sd, "%s: sensor is already on\n", __func__); 393 return 0; 394 } 395 396 if (gpio_is_valid(info->gpio_nstby)) 397 gpio_set_value(info->gpio_nstby, 0); 398 399 if (gpio_is_valid(info->gpio_nreset)) 400 gpio_set_value(info->gpio_nreset, 0); 401 402 ret = regulator_bulk_enable(NOON010_NUM_SUPPLIES, info->supply); 403 if (ret) 404 return ret; 405 406 if (gpio_is_valid(info->gpio_nreset)) { 407 msleep(50); 408 gpio_set_value(info->gpio_nreset, 1); 409 } 410 if (gpio_is_valid(info->gpio_nstby)) { 411 udelay(1000); 412 gpio_set_value(info->gpio_nstby, 1); 413 } 414 if (gpio_is_valid(info->gpio_nreset)) { 415 udelay(1000); 416 gpio_set_value(info->gpio_nreset, 0); 417 msleep(100); 418 gpio_set_value(info->gpio_nreset, 1); 419 msleep(20); 420 } 421 info->power = 1; 422 423 v4l2_dbg(1, debug, &info->sd, "%s: sensor is on\n", __func__); 424 return 0; 425} 426 427/* Called with info.lock mutex held */ 428static int power_disable(struct noon010_info *info) 429{ 430 int ret; 431 432 if (!info->power) { 433 v4l2_info(&info->sd, "%s: sensor is already off\n", __func__); 434 return 0; 435 } 436 437 ret = regulator_bulk_disable(NOON010_NUM_SUPPLIES, info->supply); 438 if (ret) 439 return ret; 440 441 if (gpio_is_valid(info->gpio_nstby)) 442 gpio_set_value(info->gpio_nstby, 0); 443 444 if (gpio_is_valid(info->gpio_nreset)) 445 gpio_set_value(info->gpio_nreset, 0); 446 447 info->power = 0; 448 449 v4l2_dbg(1, debug, &info->sd, "%s: sensor is off\n", __func__); 450 451 return 0; 452} 453 454static int noon010_s_ctrl(struct v4l2_ctrl *ctrl) 455{ 456 struct v4l2_subdev *sd = to_sd(ctrl); 457 struct noon010_info *info = to_noon010(sd); 458 int ret = 0; 459 460 v4l2_dbg(1, debug, sd, "%s: ctrl_id: %d, value: %d\n", 461 __func__, ctrl->id, ctrl->val); 462 463 mutex_lock(&info->lock); 464 /* 465 * If the device is not powered up by the host driver do 466 * not apply any controls to H/W at this time. Instead 467 * the controls will be restored right after power-up. 468 */ 469 if (!info->power) 470 goto unlock; 471 472 switch (ctrl->id) { 473 case V4L2_CID_AUTO_WHITE_BALANCE: 474 ret = noon010_enable_autowhitebalance(sd, ctrl->val); 475 break; 476 case V4L2_CID_BLUE_BALANCE: 477 ret = cam_i2c_write(sd, MWB_BGAIN_REG, ctrl->val); 478 break; 479 case V4L2_CID_RED_BALANCE: 480 ret = cam_i2c_write(sd, MWB_RGAIN_REG, ctrl->val); 481 break; 482 default: 483 ret = -EINVAL; 484 } 485unlock: 486 mutex_unlock(&info->lock); 487 return ret; 488} 489 490static int noon010_enum_mbus_code(struct v4l2_subdev *sd, 491 struct v4l2_subdev_pad_config *cfg, 492 struct v4l2_subdev_mbus_code_enum *code) 493{ 494 if (code->index >= ARRAY_SIZE(noon010_formats)) 495 return -EINVAL; 496 497 code->code = noon010_formats[code->index].code; 498 return 0; 499} 500 501static int noon010_get_fmt(struct v4l2_subdev *sd, 502 struct v4l2_subdev_pad_config *cfg, 503 struct v4l2_subdev_format *fmt) 504{ 505 struct noon010_info *info = to_noon010(sd); 506 struct v4l2_mbus_framefmt *mf; 507 508 if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { 509 if (cfg) { 510 mf = v4l2_subdev_get_try_format(sd, cfg, 0); 511 fmt->format = *mf; 512 } 513 return 0; 514 } 515 mf = &fmt->format; 516 517 mutex_lock(&info->lock); 518 mf->width = info->curr_win->width; 519 mf->height = info->curr_win->height; 520 mf->code = info->curr_fmt->code; 521 mf->colorspace = info->curr_fmt->colorspace; 522 mf->field = V4L2_FIELD_NONE; 523 524 mutex_unlock(&info->lock); 525 return 0; 526} 527 528/* Return nearest media bus frame format. */ 529static const struct noon010_format *noon010_try_fmt(struct v4l2_subdev *sd, 530 struct v4l2_mbus_framefmt *mf) 531{ 532 int i = ARRAY_SIZE(noon010_formats); 533 534 while (--i) 535 if (mf->code == noon010_formats[i].code) 536 break; 537 mf->code = noon010_formats[i].code; 538 539 return &noon010_formats[i]; 540} 541 542static int noon010_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, 543 struct v4l2_subdev_format *fmt) 544{ 545 struct noon010_info *info = to_noon010(sd); 546 const struct noon010_frmsize *size = NULL; 547 const struct noon010_format *nf; 548 struct v4l2_mbus_framefmt *mf; 549 int ret = 0; 550 551 nf = noon010_try_fmt(sd, &fmt->format); 552 noon010_try_frame_size(&fmt->format, &size); 553 fmt->format.colorspace = V4L2_COLORSPACE_JPEG; 554 fmt->format.field = V4L2_FIELD_NONE; 555 556 if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { 557 if (cfg) { 558 mf = v4l2_subdev_get_try_format(sd, cfg, 0); 559 *mf = fmt->format; 560 } 561 return 0; 562 } 563 mutex_lock(&info->lock); 564 if (!info->streaming) { 565 info->apply_new_cfg = 1; 566 info->curr_fmt = nf; 567 info->curr_win = size; 568 } else { 569 ret = -EBUSY; 570 } 571 mutex_unlock(&info->lock); 572 return ret; 573} 574 575/* Called with struct noon010_info.lock mutex held */ 576static int noon010_base_config(struct v4l2_subdev *sd) 577{ 578 int ret = noon010_bulk_write_reg(sd, noon010_base_regs); 579 if (!ret) 580 ret = noon010_set_params(sd); 581 if (!ret) 582 ret = noon010_set_flip(sd, 1, 0); 583 584 return ret; 585} 586 587static int noon010_s_power(struct v4l2_subdev *sd, int on) 588{ 589 struct noon010_info *info = to_noon010(sd); 590 int ret; 591 592 mutex_lock(&info->lock); 593 if (on) { 594 ret = power_enable(info); 595 if (!ret) 596 ret = noon010_base_config(sd); 597 } else { 598 noon010_power_ctrl(sd, false, true); 599 ret = power_disable(info); 600 } 601 mutex_unlock(&info->lock); 602 603 /* Restore the controls state */ 604 if (!ret && on) 605 ret = v4l2_ctrl_handler_setup(&info->hdl); 606 607 return ret; 608} 609 610static int noon010_s_stream(struct v4l2_subdev *sd, int on) 611{ 612 struct noon010_info *info = to_noon010(sd); 613 int ret = 0; 614 615 mutex_lock(&info->lock); 616 if (!info->streaming != !on) { 617 ret = noon010_power_ctrl(sd, false, !on); 618 if (!ret) 619 info->streaming = on; 620 } 621 if (!ret && on && info->apply_new_cfg) { 622 ret = noon010_set_params(sd); 623 if (!ret) 624 info->apply_new_cfg = 0; 625 } 626 mutex_unlock(&info->lock); 627 return ret; 628} 629 630static int noon010_log_status(struct v4l2_subdev *sd) 631{ 632 struct noon010_info *info = to_noon010(sd); 633 634 v4l2_ctrl_handler_log_status(&info->hdl, sd->name); 635 return 0; 636} 637 638static int noon010_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 639{ 640 struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(sd, fh->pad, 0); 641 642 mf->width = noon010_sizes[0].width; 643 mf->height = noon010_sizes[0].height; 644 mf->code = noon010_formats[0].code; 645 mf->colorspace = V4L2_COLORSPACE_JPEG; 646 mf->field = V4L2_FIELD_NONE; 647 return 0; 648} 649 650static const struct v4l2_subdev_internal_ops noon010_subdev_internal_ops = { 651 .open = noon010_open, 652}; 653 654static const struct v4l2_ctrl_ops noon010_ctrl_ops = { 655 .s_ctrl = noon010_s_ctrl, 656}; 657 658static const struct v4l2_subdev_core_ops noon010_core_ops = { 659 .s_power = noon010_s_power, 660 .log_status = noon010_log_status, 661}; 662 663static const struct v4l2_subdev_pad_ops noon010_pad_ops = { 664 .enum_mbus_code = noon010_enum_mbus_code, 665 .get_fmt = noon010_get_fmt, 666 .set_fmt = noon010_set_fmt, 667}; 668 669static const struct v4l2_subdev_video_ops noon010_video_ops = { 670 .s_stream = noon010_s_stream, 671}; 672 673static const struct v4l2_subdev_ops noon010_ops = { 674 .core = &noon010_core_ops, 675 .pad = &noon010_pad_ops, 676 .video = &noon010_video_ops, 677}; 678 679/* Return 0 if NOON010PC30L sensor type was detected or -ENODEV otherwise. */ 680static int noon010_detect(struct i2c_client *client, struct noon010_info *info) 681{ 682 int ret; 683 684 ret = power_enable(info); 685 if (ret) 686 return ret; 687 688 ret = i2c_smbus_read_byte_data(client, DEVICE_ID_REG); 689 if (ret < 0) 690 dev_err(&client->dev, "I2C read failed: 0x%X\n", ret); 691 692 power_disable(info); 693 694 return ret == NOON010PC30_ID ? 0 : -ENODEV; 695} 696 697static int noon010_probe(struct i2c_client *client, 698 const struct i2c_device_id *id) 699{ 700 struct noon010_info *info; 701 struct v4l2_subdev *sd; 702 const struct noon010pc30_platform_data *pdata 703 = client->dev.platform_data; 704 int ret; 705 int i; 706 707 if (!pdata) { 708 dev_err(&client->dev, "No platform data!\n"); 709 return -EIO; 710 } 711 712 info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); 713 if (!info) 714 return -ENOMEM; 715 716 mutex_init(&info->lock); 717 sd = &info->sd; 718 v4l2_i2c_subdev_init(sd, client, &noon010_ops); 719 /* Static name; NEVER use in new drivers! */ 720 strscpy(sd->name, MODULE_NAME, sizeof(sd->name)); 721 722 sd->internal_ops = &noon010_subdev_internal_ops; 723 sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 724 725 v4l2_ctrl_handler_init(&info->hdl, 3); 726 727 v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, 728 V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); 729 v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, 730 V4L2_CID_RED_BALANCE, 0, 127, 1, 64); 731 v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, 732 V4L2_CID_BLUE_BALANCE, 0, 127, 1, 64); 733 734 sd->ctrl_handler = &info->hdl; 735 736 ret = info->hdl.error; 737 if (ret) 738 goto np_err; 739 740 info->i2c_reg_page = -1; 741 info->gpio_nreset = -EINVAL; 742 info->gpio_nstby = -EINVAL; 743 info->curr_fmt = &noon010_formats[0]; 744 info->curr_win = &noon010_sizes[0]; 745 746 if (gpio_is_valid(pdata->gpio_nreset)) { 747 ret = devm_gpio_request_one(&client->dev, pdata->gpio_nreset, 748 GPIOF_OUT_INIT_LOW, 749 "NOON010PC30 NRST"); 750 if (ret) { 751 dev_err(&client->dev, "GPIO request error: %d\n", ret); 752 goto np_err; 753 } 754 info->gpio_nreset = pdata->gpio_nreset; 755 gpio_export(info->gpio_nreset, 0); 756 } 757 758 if (gpio_is_valid(pdata->gpio_nstby)) { 759 ret = devm_gpio_request_one(&client->dev, pdata->gpio_nstby, 760 GPIOF_OUT_INIT_LOW, 761 "NOON010PC30 NSTBY"); 762 if (ret) { 763 dev_err(&client->dev, "GPIO request error: %d\n", ret); 764 goto np_err; 765 } 766 info->gpio_nstby = pdata->gpio_nstby; 767 gpio_export(info->gpio_nstby, 0); 768 } 769 770 for (i = 0; i < NOON010_NUM_SUPPLIES; i++) 771 info->supply[i].supply = noon010_supply_name[i]; 772 773 ret = devm_regulator_bulk_get(&client->dev, NOON010_NUM_SUPPLIES, 774 info->supply); 775 if (ret) 776 goto np_err; 777 778 info->pad.flags = MEDIA_PAD_FL_SOURCE; 779 sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; 780 ret = media_entity_pads_init(&sd->entity, 1, &info->pad); 781 if (ret < 0) 782 goto np_err; 783 784 ret = noon010_detect(client, info); 785 if (!ret) 786 return 0; 787 788np_err: 789 v4l2_ctrl_handler_free(&info->hdl); 790 v4l2_device_unregister_subdev(sd); 791 return ret; 792} 793 794static int noon010_remove(struct i2c_client *client) 795{ 796 struct v4l2_subdev *sd = i2c_get_clientdata(client); 797 struct noon010_info *info = to_noon010(sd); 798 799 v4l2_device_unregister_subdev(sd); 800 v4l2_ctrl_handler_free(&info->hdl); 801 media_entity_cleanup(&sd->entity); 802 803 return 0; 804} 805 806static const struct i2c_device_id noon010_id[] = { 807 { MODULE_NAME, 0 }, 808 { }, 809}; 810MODULE_DEVICE_TABLE(i2c, noon010_id); 811 812 813static struct i2c_driver noon010_i2c_driver = { 814 .driver = { 815 .name = MODULE_NAME 816 }, 817 .probe = noon010_probe, 818 .remove = noon010_remove, 819 .id_table = noon010_id, 820}; 821 822module_i2c_driver(noon010_i2c_driver); 823 824MODULE_DESCRIPTION("Siliconfile NOON010PC30 camera driver"); 825MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); 826MODULE_LICENSE("GPL"); 827