noon010pc30.c (20414B)
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/consumer.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 struct gpio_desc *reset; 134 struct gpio_desc *stby; 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 /* Assert standby: line should be flagged active low in descriptor */ 397 if (info->stby) 398 gpiod_set_value(info->stby, 1); 399 400 /* Assert reset: line should be flagged active low in descriptor */ 401 if (info->reset) 402 gpiod_set_value(info->reset, 1); 403 404 ret = regulator_bulk_enable(NOON010_NUM_SUPPLIES, info->supply); 405 if (ret) 406 return ret; 407 408 /* De-assert reset and standby */ 409 if (info->reset) { 410 msleep(50); 411 gpiod_set_value(info->reset, 0); 412 } 413 if (info->stby) { 414 udelay(1000); 415 gpiod_set_value(info->stby, 0); 416 } 417 /* Cycle reset: assert and deassert */ 418 if (info->reset) { 419 udelay(1000); 420 gpiod_set_value(info->reset, 1); 421 msleep(100); 422 gpiod_set_value(info->reset, 0); 423 msleep(20); 424 } 425 info->power = 1; 426 427 v4l2_dbg(1, debug, &info->sd, "%s: sensor is on\n", __func__); 428 return 0; 429} 430 431/* Called with info.lock mutex held */ 432static int power_disable(struct noon010_info *info) 433{ 434 int ret; 435 436 if (!info->power) { 437 v4l2_info(&info->sd, "%s: sensor is already off\n", __func__); 438 return 0; 439 } 440 441 ret = regulator_bulk_disable(NOON010_NUM_SUPPLIES, info->supply); 442 if (ret) 443 return ret; 444 445 /* Assert standby and reset */ 446 if (info->stby) 447 gpiod_set_value(info->stby, 1); 448 449 if (info->reset) 450 gpiod_set_value(info->reset, 1); 451 452 info->power = 0; 453 454 v4l2_dbg(1, debug, &info->sd, "%s: sensor is off\n", __func__); 455 456 return 0; 457} 458 459static int noon010_s_ctrl(struct v4l2_ctrl *ctrl) 460{ 461 struct v4l2_subdev *sd = to_sd(ctrl); 462 struct noon010_info *info = to_noon010(sd); 463 int ret = 0; 464 465 v4l2_dbg(1, debug, sd, "%s: ctrl_id: %d, value: %d\n", 466 __func__, ctrl->id, ctrl->val); 467 468 mutex_lock(&info->lock); 469 /* 470 * If the device is not powered up by the host driver do 471 * not apply any controls to H/W at this time. Instead 472 * the controls will be restored right after power-up. 473 */ 474 if (!info->power) 475 goto unlock; 476 477 switch (ctrl->id) { 478 case V4L2_CID_AUTO_WHITE_BALANCE: 479 ret = noon010_enable_autowhitebalance(sd, ctrl->val); 480 break; 481 case V4L2_CID_BLUE_BALANCE: 482 ret = cam_i2c_write(sd, MWB_BGAIN_REG, ctrl->val); 483 break; 484 case V4L2_CID_RED_BALANCE: 485 ret = cam_i2c_write(sd, MWB_RGAIN_REG, ctrl->val); 486 break; 487 default: 488 ret = -EINVAL; 489 } 490unlock: 491 mutex_unlock(&info->lock); 492 return ret; 493} 494 495static int noon010_enum_mbus_code(struct v4l2_subdev *sd, 496 struct v4l2_subdev_state *sd_state, 497 struct v4l2_subdev_mbus_code_enum *code) 498{ 499 if (code->index >= ARRAY_SIZE(noon010_formats)) 500 return -EINVAL; 501 502 code->code = noon010_formats[code->index].code; 503 return 0; 504} 505 506static int noon010_get_fmt(struct v4l2_subdev *sd, 507 struct v4l2_subdev_state *sd_state, 508 struct v4l2_subdev_format *fmt) 509{ 510 struct noon010_info *info = to_noon010(sd); 511 struct v4l2_mbus_framefmt *mf; 512 513 if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { 514 if (sd_state) { 515 mf = v4l2_subdev_get_try_format(sd, sd_state, 0); 516 fmt->format = *mf; 517 } 518 return 0; 519 } 520 mf = &fmt->format; 521 522 mutex_lock(&info->lock); 523 mf->width = info->curr_win->width; 524 mf->height = info->curr_win->height; 525 mf->code = info->curr_fmt->code; 526 mf->colorspace = info->curr_fmt->colorspace; 527 mf->field = V4L2_FIELD_NONE; 528 529 mutex_unlock(&info->lock); 530 return 0; 531} 532 533/* Return nearest media bus frame format. */ 534static const struct noon010_format *noon010_try_fmt(struct v4l2_subdev *sd, 535 struct v4l2_mbus_framefmt *mf) 536{ 537 int i = ARRAY_SIZE(noon010_formats); 538 539 while (--i) 540 if (mf->code == noon010_formats[i].code) 541 break; 542 mf->code = noon010_formats[i].code; 543 544 return &noon010_formats[i]; 545} 546 547static int noon010_set_fmt(struct v4l2_subdev *sd, 548 struct v4l2_subdev_state *sd_state, 549 struct v4l2_subdev_format *fmt) 550{ 551 struct noon010_info *info = to_noon010(sd); 552 const struct noon010_frmsize *size = NULL; 553 const struct noon010_format *nf; 554 struct v4l2_mbus_framefmt *mf; 555 int ret = 0; 556 557 nf = noon010_try_fmt(sd, &fmt->format); 558 noon010_try_frame_size(&fmt->format, &size); 559 fmt->format.colorspace = V4L2_COLORSPACE_JPEG; 560 fmt->format.field = V4L2_FIELD_NONE; 561 562 if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { 563 if (sd_state) { 564 mf = v4l2_subdev_get_try_format(sd, sd_state, 0); 565 *mf = fmt->format; 566 } 567 return 0; 568 } 569 mutex_lock(&info->lock); 570 if (!info->streaming) { 571 info->apply_new_cfg = 1; 572 info->curr_fmt = nf; 573 info->curr_win = size; 574 } else { 575 ret = -EBUSY; 576 } 577 mutex_unlock(&info->lock); 578 return ret; 579} 580 581/* Called with struct noon010_info.lock mutex held */ 582static int noon010_base_config(struct v4l2_subdev *sd) 583{ 584 int ret = noon010_bulk_write_reg(sd, noon010_base_regs); 585 if (!ret) 586 ret = noon010_set_params(sd); 587 if (!ret) 588 ret = noon010_set_flip(sd, 1, 0); 589 590 return ret; 591} 592 593static int noon010_s_power(struct v4l2_subdev *sd, int on) 594{ 595 struct noon010_info *info = to_noon010(sd); 596 int ret; 597 598 mutex_lock(&info->lock); 599 if (on) { 600 ret = power_enable(info); 601 if (!ret) 602 ret = noon010_base_config(sd); 603 } else { 604 noon010_power_ctrl(sd, false, true); 605 ret = power_disable(info); 606 } 607 mutex_unlock(&info->lock); 608 609 /* Restore the controls state */ 610 if (!ret && on) 611 ret = v4l2_ctrl_handler_setup(&info->hdl); 612 613 return ret; 614} 615 616static int noon010_s_stream(struct v4l2_subdev *sd, int on) 617{ 618 struct noon010_info *info = to_noon010(sd); 619 int ret = 0; 620 621 mutex_lock(&info->lock); 622 if (!info->streaming != !on) { 623 ret = noon010_power_ctrl(sd, false, !on); 624 if (!ret) 625 info->streaming = on; 626 } 627 if (!ret && on && info->apply_new_cfg) { 628 ret = noon010_set_params(sd); 629 if (!ret) 630 info->apply_new_cfg = 0; 631 } 632 mutex_unlock(&info->lock); 633 return ret; 634} 635 636static int noon010_log_status(struct v4l2_subdev *sd) 637{ 638 struct noon010_info *info = to_noon010(sd); 639 640 v4l2_ctrl_handler_log_status(&info->hdl, sd->name); 641 return 0; 642} 643 644static int noon010_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 645{ 646 struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(sd, 647 fh->state, 648 0); 649 650 mf->width = noon010_sizes[0].width; 651 mf->height = noon010_sizes[0].height; 652 mf->code = noon010_formats[0].code; 653 mf->colorspace = V4L2_COLORSPACE_JPEG; 654 mf->field = V4L2_FIELD_NONE; 655 return 0; 656} 657 658static const struct v4l2_subdev_internal_ops noon010_subdev_internal_ops = { 659 .open = noon010_open, 660}; 661 662static const struct v4l2_ctrl_ops noon010_ctrl_ops = { 663 .s_ctrl = noon010_s_ctrl, 664}; 665 666static const struct v4l2_subdev_core_ops noon010_core_ops = { 667 .s_power = noon010_s_power, 668 .log_status = noon010_log_status, 669}; 670 671static const struct v4l2_subdev_pad_ops noon010_pad_ops = { 672 .enum_mbus_code = noon010_enum_mbus_code, 673 .get_fmt = noon010_get_fmt, 674 .set_fmt = noon010_set_fmt, 675}; 676 677static const struct v4l2_subdev_video_ops noon010_video_ops = { 678 .s_stream = noon010_s_stream, 679}; 680 681static const struct v4l2_subdev_ops noon010_ops = { 682 .core = &noon010_core_ops, 683 .pad = &noon010_pad_ops, 684 .video = &noon010_video_ops, 685}; 686 687/* Return 0 if NOON010PC30L sensor type was detected or -ENODEV otherwise. */ 688static int noon010_detect(struct i2c_client *client, struct noon010_info *info) 689{ 690 int ret; 691 692 ret = power_enable(info); 693 if (ret) 694 return ret; 695 696 ret = i2c_smbus_read_byte_data(client, DEVICE_ID_REG); 697 if (ret < 0) 698 dev_err(&client->dev, "I2C read failed: 0x%X\n", ret); 699 700 power_disable(info); 701 702 return ret == NOON010PC30_ID ? 0 : -ENODEV; 703} 704 705static int noon010_probe(struct i2c_client *client, 706 const struct i2c_device_id *id) 707{ 708 struct noon010_info *info; 709 struct v4l2_subdev *sd; 710 const struct noon010pc30_platform_data *pdata 711 = client->dev.platform_data; 712 int ret; 713 int i; 714 715 if (!pdata) { 716 dev_err(&client->dev, "No platform data!\n"); 717 return -EIO; 718 } 719 720 info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); 721 if (!info) 722 return -ENOMEM; 723 724 mutex_init(&info->lock); 725 sd = &info->sd; 726 v4l2_i2c_subdev_init(sd, client, &noon010_ops); 727 /* Static name; NEVER use in new drivers! */ 728 strscpy(sd->name, MODULE_NAME, sizeof(sd->name)); 729 730 sd->internal_ops = &noon010_subdev_internal_ops; 731 sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 732 733 v4l2_ctrl_handler_init(&info->hdl, 3); 734 735 v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, 736 V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); 737 v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, 738 V4L2_CID_RED_BALANCE, 0, 127, 1, 64); 739 v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, 740 V4L2_CID_BLUE_BALANCE, 0, 127, 1, 64); 741 742 sd->ctrl_handler = &info->hdl; 743 744 ret = info->hdl.error; 745 if (ret) 746 goto np_err; 747 748 info->i2c_reg_page = -1; 749 info->curr_fmt = &noon010_formats[0]; 750 info->curr_win = &noon010_sizes[0]; 751 752 /* Request reset asserted so we get put into reset */ 753 info->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_HIGH); 754 if (IS_ERR(info->reset)) { 755 ret = PTR_ERR(info->reset); 756 goto np_err; 757 } 758 gpiod_set_consumer_name(info->reset, "NOON010PC30 NRST"); 759 760 /* Request standby asserted so we get put into standby */ 761 info->stby = devm_gpiod_get(&client->dev, "standby", GPIOD_OUT_HIGH); 762 if (IS_ERR(info->stby)) { 763 ret = PTR_ERR(info->stby); 764 goto np_err; 765 } 766 gpiod_set_consumer_name(info->reset, "NOON010PC30 STBY"); 767 768 for (i = 0; i < NOON010_NUM_SUPPLIES; i++) 769 info->supply[i].supply = noon010_supply_name[i]; 770 771 ret = devm_regulator_bulk_get(&client->dev, NOON010_NUM_SUPPLIES, 772 info->supply); 773 if (ret) 774 goto np_err; 775 776 info->pad.flags = MEDIA_PAD_FL_SOURCE; 777 sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; 778 ret = media_entity_pads_init(&sd->entity, 1, &info->pad); 779 if (ret < 0) 780 goto np_err; 781 782 ret = noon010_detect(client, info); 783 if (!ret) 784 return 0; 785 786np_err: 787 v4l2_ctrl_handler_free(&info->hdl); 788 v4l2_device_unregister_subdev(sd); 789 return ret; 790} 791 792static int noon010_remove(struct i2c_client *client) 793{ 794 struct v4l2_subdev *sd = i2c_get_clientdata(client); 795 struct noon010_info *info = to_noon010(sd); 796 797 v4l2_device_unregister_subdev(sd); 798 v4l2_ctrl_handler_free(&info->hdl); 799 media_entity_cleanup(&sd->entity); 800 801 return 0; 802} 803 804static const struct i2c_device_id noon010_id[] = { 805 { MODULE_NAME, 0 }, 806 { }, 807}; 808MODULE_DEVICE_TABLE(i2c, noon010_id); 809 810 811static struct i2c_driver noon010_i2c_driver = { 812 .driver = { 813 .name = MODULE_NAME 814 }, 815 .probe = noon010_probe, 816 .remove = noon010_remove, 817 .id_table = noon010_id, 818}; 819 820module_i2c_driver(noon010_i2c_driver); 821 822MODULE_DESCRIPTION("Siliconfile NOON010PC30 camera driver"); 823MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); 824MODULE_LICENSE("GPL");