1/* 2 * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver 3 * 4 * Copyright (C) 2013 Samsung Electronics Co., Ltd. 5 * 6 * Authors: Sylwester Nawrocki <s.nawrocki@samsung.com> 7 * Younghwan Joo <yhwan.joo@samsung.com> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2 as 11 * published by the Free Software Foundation. 12 */ 13#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ 14 15#include <linux/device.h> 16#include <linux/errno.h> 17#include <linux/kernel.h> 18#include <linux/list.h> 19#include <linux/module.h> 20#include <linux/platform_device.h> 21#include <linux/printk.h> 22#include <linux/pm_runtime.h> 23#include <linux/slab.h> 24#include <linux/types.h> 25#include <media/v4l2-device.h> 26 27#include "media-dev.h" 28#include "fimc-isp-video.h" 29#include "fimc-is-command.h" 30#include "fimc-is-param.h" 31#include "fimc-is-regs.h" 32#include "fimc-is.h" 33 34int fimc_isp_debug; 35module_param_named(debug_isp, fimc_isp_debug, int, S_IRUGO | S_IWUSR); 36 37static const struct fimc_fmt fimc_isp_formats[FIMC_ISP_NUM_FORMATS] = { 38 { 39 .name = "RAW8 (GRBG)", 40 .fourcc = V4L2_PIX_FMT_SGRBG8, 41 .depth = { 8 }, 42 .color = FIMC_FMT_RAW8, 43 .memplanes = 1, 44 .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, 45 }, { 46 .name = "RAW10 (GRBG)", 47 .fourcc = V4L2_PIX_FMT_SGRBG10, 48 .depth = { 10 }, 49 .color = FIMC_FMT_RAW10, 50 .memplanes = 1, 51 .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, 52 }, { 53 .name = "RAW12 (GRBG)", 54 .fourcc = V4L2_PIX_FMT_SGRBG12, 55 .depth = { 12 }, 56 .color = FIMC_FMT_RAW12, 57 .memplanes = 1, 58 .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, 59 }, 60}; 61 62/** 63 * fimc_isp_find_format - lookup color format by fourcc or media bus code 64 * @pixelformat: fourcc to match, ignored if null 65 * @mbus_code: media bus code to match, ignored if null 66 * @index: index to the fimc_isp_formats array, ignored if negative 67 */ 68const struct fimc_fmt *fimc_isp_find_format(const u32 *pixelformat, 69 const u32 *mbus_code, int index) 70{ 71 const struct fimc_fmt *fmt, *def_fmt = NULL; 72 unsigned int i; 73 int id = 0; 74 75 if (index >= (int)ARRAY_SIZE(fimc_isp_formats)) 76 return NULL; 77 78 for (i = 0; i < ARRAY_SIZE(fimc_isp_formats); ++i) { 79 fmt = &fimc_isp_formats[i]; 80 if (pixelformat && fmt->fourcc == *pixelformat) 81 return fmt; 82 if (mbus_code && fmt->mbus_code == *mbus_code) 83 return fmt; 84 if (index == id) 85 def_fmt = fmt; 86 id++; 87 } 88 return def_fmt; 89} 90 91void fimc_isp_irq_handler(struct fimc_is *is) 92{ 93 is->i2h_cmd.args[0] = mcuctl_read(is, MCUCTL_REG_ISSR(20)); 94 is->i2h_cmd.args[1] = mcuctl_read(is, MCUCTL_REG_ISSR(21)); 95 96 fimc_is_fw_clear_irq1(is, FIMC_IS_INT_FRAME_DONE_ISP); 97 fimc_isp_video_irq_handler(is); 98 99 wake_up(&is->irq_queue); 100} 101 102/* Capture subdev media entity operations */ 103static int fimc_is_link_setup(struct media_entity *entity, 104 const struct media_pad *local, 105 const struct media_pad *remote, u32 flags) 106{ 107 return 0; 108} 109 110static const struct media_entity_operations fimc_is_subdev_media_ops = { 111 .link_setup = fimc_is_link_setup, 112}; 113 114static int fimc_is_subdev_enum_mbus_code(struct v4l2_subdev *sd, 115 struct v4l2_subdev_pad_config *cfg, 116 struct v4l2_subdev_mbus_code_enum *code) 117{ 118 const struct fimc_fmt *fmt; 119 120 fmt = fimc_isp_find_format(NULL, NULL, code->index); 121 if (!fmt) 122 return -EINVAL; 123 code->code = fmt->mbus_code; 124 return 0; 125} 126 127static int fimc_isp_subdev_get_fmt(struct v4l2_subdev *sd, 128 struct v4l2_subdev_pad_config *cfg, 129 struct v4l2_subdev_format *fmt) 130{ 131 struct fimc_isp *isp = v4l2_get_subdevdata(sd); 132 struct v4l2_mbus_framefmt *mf = &fmt->format; 133 134 if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { 135 *mf = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); 136 return 0; 137 } 138 139 mf->colorspace = V4L2_COLORSPACE_SRGB; 140 141 mutex_lock(&isp->subdev_lock); 142 143 if (fmt->pad == FIMC_ISP_SD_PAD_SINK) { 144 /* ISP OTF input image format */ 145 *mf = isp->sink_fmt; 146 } else { 147 /* ISP OTF output image format */ 148 *mf = isp->src_fmt; 149 150 if (fmt->pad == FIMC_ISP_SD_PAD_SRC_FIFO) { 151 mf->colorspace = V4L2_COLORSPACE_JPEG; 152 mf->code = MEDIA_BUS_FMT_YUV10_1X30; 153 } 154 } 155 156 mutex_unlock(&isp->subdev_lock); 157 158 isp_dbg(1, sd, "%s: pad%d: fmt: 0x%x, %dx%d\n", __func__, 159 fmt->pad, mf->code, mf->width, mf->height); 160 161 return 0; 162} 163 164static void __isp_subdev_try_format(struct fimc_isp *isp, 165 struct v4l2_subdev_pad_config *cfg, 166 struct v4l2_subdev_format *fmt) 167{ 168 struct v4l2_mbus_framefmt *mf = &fmt->format; 169 struct v4l2_mbus_framefmt *format; 170 171 mf->colorspace = V4L2_COLORSPACE_SRGB; 172 173 if (fmt->pad == FIMC_ISP_SD_PAD_SINK) { 174 v4l_bound_align_image(&mf->width, FIMC_ISP_SINK_WIDTH_MIN, 175 FIMC_ISP_SINK_WIDTH_MAX, 0, 176 &mf->height, FIMC_ISP_SINK_HEIGHT_MIN, 177 FIMC_ISP_SINK_HEIGHT_MAX, 0, 0); 178 mf->code = MEDIA_BUS_FMT_SGRBG10_1X10; 179 } else { 180 if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) 181 format = v4l2_subdev_get_try_format(&isp->subdev, cfg, 182 FIMC_ISP_SD_PAD_SINK); 183 else 184 format = &isp->sink_fmt; 185 186 /* Allow changing format only on sink pad */ 187 mf->width = format->width - FIMC_ISP_CAC_MARGIN_WIDTH; 188 mf->height = format->height - FIMC_ISP_CAC_MARGIN_HEIGHT; 189 190 if (fmt->pad == FIMC_ISP_SD_PAD_SRC_FIFO) { 191 mf->code = MEDIA_BUS_FMT_YUV10_1X30; 192 mf->colorspace = V4L2_COLORSPACE_JPEG; 193 } else { 194 mf->code = format->code; 195 } 196 } 197} 198 199static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd, 200 struct v4l2_subdev_pad_config *cfg, 201 struct v4l2_subdev_format *fmt) 202{ 203 struct fimc_isp *isp = v4l2_get_subdevdata(sd); 204 struct fimc_is *is = fimc_isp_to_is(isp); 205 struct v4l2_mbus_framefmt *mf = &fmt->format; 206 int ret = 0; 207 208 isp_dbg(1, sd, "%s: pad%d: code: 0x%x, %dx%d\n", 209 __func__, fmt->pad, mf->code, mf->width, mf->height); 210 211 mutex_lock(&isp->subdev_lock); 212 __isp_subdev_try_format(isp, cfg, fmt); 213 214 if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { 215 mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); 216 *mf = fmt->format; 217 218 /* Propagate format to the source pads */ 219 if (fmt->pad == FIMC_ISP_SD_PAD_SINK) { 220 struct v4l2_subdev_format format = *fmt; 221 unsigned int pad; 222 223 for (pad = FIMC_ISP_SD_PAD_SRC_FIFO; 224 pad < FIMC_ISP_SD_PADS_NUM; pad++) { 225 format.pad = pad; 226 __isp_subdev_try_format(isp, cfg, &format); 227 mf = v4l2_subdev_get_try_format(sd, cfg, pad); 228 *mf = format.format; 229 } 230 } 231 } else { 232 if (sd->entity.stream_count == 0) { 233 if (fmt->pad == FIMC_ISP_SD_PAD_SINK) { 234 struct v4l2_subdev_format format = *fmt; 235 236 isp->sink_fmt = *mf; 237 238 format.pad = FIMC_ISP_SD_PAD_SRC_DMA; 239 __isp_subdev_try_format(isp, cfg, &format); 240 241 isp->src_fmt = format.format; 242 __is_set_frame_size(is, &isp->src_fmt); 243 } else { 244 isp->src_fmt = *mf; 245 } 246 } else { 247 ret = -EBUSY; 248 } 249 } 250 251 mutex_unlock(&isp->subdev_lock); 252 return ret; 253} 254 255static int fimc_isp_subdev_s_stream(struct v4l2_subdev *sd, int on) 256{ 257 struct fimc_isp *isp = v4l2_get_subdevdata(sd); 258 struct fimc_is *is = fimc_isp_to_is(isp); 259 int ret; 260 261 isp_dbg(1, sd, "%s: on: %d\n", __func__, on); 262 263 if (!test_bit(IS_ST_INIT_DONE, &is->state)) 264 return -EBUSY; 265 266 fimc_is_mem_barrier(); 267 268 if (on) { 269 if (__get_pending_param_count(is)) { 270 ret = fimc_is_itf_s_param(is, true); 271 if (ret < 0) 272 return ret; 273 } 274 275 isp_dbg(1, sd, "changing mode to %d\n", is->config_index); 276 277 ret = fimc_is_itf_mode_change(is); 278 if (ret) 279 return -EINVAL; 280 281 clear_bit(IS_ST_STREAM_ON, &is->state); 282 fimc_is_hw_stream_on(is); 283 ret = fimc_is_wait_event(is, IS_ST_STREAM_ON, 1, 284 FIMC_IS_CONFIG_TIMEOUT); 285 if (ret < 0) { 286 v4l2_err(sd, "stream on timeout\n"); 287 return ret; 288 } 289 } else { 290 clear_bit(IS_ST_STREAM_OFF, &is->state); 291 fimc_is_hw_stream_off(is); 292 ret = fimc_is_wait_event(is, IS_ST_STREAM_OFF, 1, 293 FIMC_IS_CONFIG_TIMEOUT); 294 if (ret < 0) { 295 v4l2_err(sd, "stream off timeout\n"); 296 return ret; 297 } 298 is->setfile.sub_index = 0; 299 } 300 301 return 0; 302} 303 304static int fimc_isp_subdev_s_power(struct v4l2_subdev *sd, int on) 305{ 306 struct fimc_isp *isp = v4l2_get_subdevdata(sd); 307 struct fimc_is *is = fimc_isp_to_is(isp); 308 int ret = 0; 309 310 pr_debug("on: %d\n", on); 311 312 if (on) { 313 ret = pm_runtime_get_sync(&is->pdev->dev); 314 if (ret < 0) 315 return ret; 316 set_bit(IS_ST_PWR_ON, &is->state); 317 318 ret = fimc_is_start_firmware(is); 319 if (ret < 0) { 320 v4l2_err(sd, "firmware booting failed\n"); 321 pm_runtime_put(&is->pdev->dev); 322 return ret; 323 } 324 set_bit(IS_ST_PWR_SUBIP_ON, &is->state); 325 326 ret = fimc_is_hw_initialize(is); 327 } else { 328 /* Close sensor */ 329 if (!test_bit(IS_ST_PWR_ON, &is->state)) { 330 fimc_is_hw_close_sensor(is, 0); 331 332 ret = fimc_is_wait_event(is, IS_ST_OPEN_SENSOR, 0, 333 FIMC_IS_CONFIG_TIMEOUT); 334 if (ret < 0) { 335 v4l2_err(sd, "sensor close timeout\n"); 336 return ret; 337 } 338 } 339 340 /* SUB IP power off */ 341 if (test_bit(IS_ST_PWR_SUBIP_ON, &is->state)) { 342 fimc_is_hw_subip_power_off(is); 343 ret = fimc_is_wait_event(is, IS_ST_PWR_SUBIP_ON, 0, 344 FIMC_IS_CONFIG_TIMEOUT); 345 if (ret < 0) { 346 v4l2_err(sd, "sub-IP power off timeout\n"); 347 return ret; 348 } 349 } 350 351 fimc_is_cpu_set_power(is, 0); 352 pm_runtime_put_sync(&is->pdev->dev); 353 354 clear_bit(IS_ST_PWR_ON, &is->state); 355 clear_bit(IS_ST_INIT_DONE, &is->state); 356 is->state = 0; 357 is->config[is->config_index].p_region_index[0] = 0; 358 is->config[is->config_index].p_region_index[1] = 0; 359 set_bit(IS_ST_IDLE, &is->state); 360 wmb(); 361 } 362 363 return ret; 364} 365 366static int fimc_isp_subdev_open(struct v4l2_subdev *sd, 367 struct v4l2_subdev_fh *fh) 368{ 369 struct v4l2_mbus_framefmt fmt; 370 struct v4l2_mbus_framefmt *format; 371 372 format = v4l2_subdev_get_try_format(sd, fh->pad, FIMC_ISP_SD_PAD_SINK); 373 374 fmt.colorspace = V4L2_COLORSPACE_SRGB; 375 fmt.code = fimc_isp_formats[0].mbus_code; 376 fmt.width = DEFAULT_PREVIEW_STILL_WIDTH + FIMC_ISP_CAC_MARGIN_WIDTH; 377 fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT + FIMC_ISP_CAC_MARGIN_HEIGHT; 378 fmt.field = V4L2_FIELD_NONE; 379 *format = fmt; 380 381 format = v4l2_subdev_get_try_format(sd, fh->pad, FIMC_ISP_SD_PAD_SRC_FIFO); 382 fmt.width = DEFAULT_PREVIEW_STILL_WIDTH; 383 fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT; 384 *format = fmt; 385 386 format = v4l2_subdev_get_try_format(sd, fh->pad, FIMC_ISP_SD_PAD_SRC_DMA); 387 *format = fmt; 388 389 return 0; 390} 391 392static int fimc_isp_subdev_registered(struct v4l2_subdev *sd) 393{ 394 struct fimc_isp *isp = v4l2_get_subdevdata(sd); 395 int ret; 396 397 /* Use pipeline object allocated by the media device. */ 398 isp->video_capture.ve.pipe = v4l2_get_subdev_hostdata(sd); 399 400 ret = fimc_isp_video_device_register(isp, sd->v4l2_dev, 401 V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); 402 if (ret < 0) 403 isp->video_capture.ve.pipe = NULL; 404 405 return ret; 406} 407 408static void fimc_isp_subdev_unregistered(struct v4l2_subdev *sd) 409{ 410 struct fimc_isp *isp = v4l2_get_subdevdata(sd); 411 412 fimc_isp_video_device_unregister(isp, 413 V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); 414} 415 416static const struct v4l2_subdev_internal_ops fimc_is_subdev_internal_ops = { 417 .registered = fimc_isp_subdev_registered, 418 .unregistered = fimc_isp_subdev_unregistered, 419 .open = fimc_isp_subdev_open, 420}; 421 422static const struct v4l2_subdev_pad_ops fimc_is_subdev_pad_ops = { 423 .enum_mbus_code = fimc_is_subdev_enum_mbus_code, 424 .get_fmt = fimc_isp_subdev_get_fmt, 425 .set_fmt = fimc_isp_subdev_set_fmt, 426}; 427 428static const struct v4l2_subdev_video_ops fimc_is_subdev_video_ops = { 429 .s_stream = fimc_isp_subdev_s_stream, 430}; 431 432static const struct v4l2_subdev_core_ops fimc_is_core_ops = { 433 .s_power = fimc_isp_subdev_s_power, 434}; 435 436static struct v4l2_subdev_ops fimc_is_subdev_ops = { 437 .core = &fimc_is_core_ops, 438 .video = &fimc_is_subdev_video_ops, 439 .pad = &fimc_is_subdev_pad_ops, 440}; 441 442static int __ctrl_set_white_balance(struct fimc_is *is, int value) 443{ 444 switch (value) { 445 case V4L2_WHITE_BALANCE_AUTO: 446 __is_set_isp_awb(is, ISP_AWB_COMMAND_AUTO, 0); 447 break; 448 case V4L2_WHITE_BALANCE_DAYLIGHT: 449 __is_set_isp_awb(is, ISP_AWB_COMMAND_ILLUMINATION, 450 ISP_AWB_ILLUMINATION_DAYLIGHT); 451 break; 452 case V4L2_WHITE_BALANCE_CLOUDY: 453 __is_set_isp_awb(is, ISP_AWB_COMMAND_ILLUMINATION, 454 ISP_AWB_ILLUMINATION_CLOUDY); 455 break; 456 case V4L2_WHITE_BALANCE_INCANDESCENT: 457 __is_set_isp_awb(is, ISP_AWB_COMMAND_ILLUMINATION, 458 ISP_AWB_ILLUMINATION_TUNGSTEN); 459 break; 460 case V4L2_WHITE_BALANCE_FLUORESCENT: 461 __is_set_isp_awb(is, ISP_AWB_COMMAND_ILLUMINATION, 462 ISP_AWB_ILLUMINATION_FLUORESCENT); 463 break; 464 default: 465 return -EINVAL; 466 } 467 468 return 0; 469} 470 471static int __ctrl_set_aewb_lock(struct fimc_is *is, 472 struct v4l2_ctrl *ctrl) 473{ 474 bool awb_lock = ctrl->val & V4L2_LOCK_WHITE_BALANCE; 475 bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE; 476 struct isp_param *isp = &is->is_p_region->parameter.isp; 477 int cmd, ret; 478 479 cmd = ae_lock ? ISP_AA_COMMAND_STOP : ISP_AA_COMMAND_START; 480 isp->aa.cmd = cmd; 481 isp->aa.target = ISP_AA_TARGET_AE; 482 fimc_is_set_param_bit(is, PARAM_ISP_AA); 483 is->af.ae_lock_state = ae_lock; 484 wmb(); 485 486 ret = fimc_is_itf_s_param(is, false); 487 if (ret < 0) 488 return ret; 489 490 cmd = awb_lock ? ISP_AA_COMMAND_STOP : ISP_AA_COMMAND_START; 491 isp->aa.cmd = cmd; 492 isp->aa.target = ISP_AA_TARGET_AE; 493 fimc_is_set_param_bit(is, PARAM_ISP_AA); 494 is->af.awb_lock_state = awb_lock; 495 wmb(); 496 497 return fimc_is_itf_s_param(is, false); 498} 499 500/* Supported manual ISO values */ 501static const s64 iso_qmenu[] = { 502 50, 100, 200, 400, 800, 503}; 504 505static int __ctrl_set_iso(struct fimc_is *is, int value) 506{ 507 unsigned int idx, iso; 508 509 if (value == V4L2_ISO_SENSITIVITY_AUTO) { 510 __is_set_isp_iso(is, ISP_ISO_COMMAND_AUTO, 0); 511 return 0; 512 } 513 idx = is->isp.ctrls.iso->val; 514 if (idx >= ARRAY_SIZE(iso_qmenu)) 515 return -EINVAL; 516 517 iso = iso_qmenu[idx]; 518 __is_set_isp_iso(is, ISP_ISO_COMMAND_MANUAL, iso); 519 return 0; 520} 521 522static int __ctrl_set_metering(struct fimc_is *is, unsigned int value) 523{ 524 unsigned int val; 525 526 switch (value) { 527 case V4L2_EXPOSURE_METERING_AVERAGE: 528 val = ISP_METERING_COMMAND_AVERAGE; 529 break; 530 case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: 531 val = ISP_METERING_COMMAND_CENTER; 532 break; 533 case V4L2_EXPOSURE_METERING_SPOT: 534 val = ISP_METERING_COMMAND_SPOT; 535 break; 536 case V4L2_EXPOSURE_METERING_MATRIX: 537 val = ISP_METERING_COMMAND_MATRIX; 538 break; 539 default: 540 return -EINVAL; 541 } 542 543 __is_set_isp_metering(is, IS_METERING_CONFIG_CMD, val); 544 return 0; 545} 546 547static int __ctrl_set_afc(struct fimc_is *is, int value) 548{ 549 switch (value) { 550 case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED: 551 __is_set_isp_afc(is, ISP_AFC_COMMAND_DISABLE, 0); 552 break; 553 case V4L2_CID_POWER_LINE_FREQUENCY_50HZ: 554 __is_set_isp_afc(is, ISP_AFC_COMMAND_MANUAL, 50); 555 break; 556 case V4L2_CID_POWER_LINE_FREQUENCY_60HZ: 557 __is_set_isp_afc(is, ISP_AFC_COMMAND_MANUAL, 60); 558 break; 559 case V4L2_CID_POWER_LINE_FREQUENCY_AUTO: 560 __is_set_isp_afc(is, ISP_AFC_COMMAND_AUTO, 0); 561 break; 562 default: 563 return -EINVAL; 564 } 565 566 return 0; 567} 568 569static int __ctrl_set_image_effect(struct fimc_is *is, int value) 570{ 571 static const u8 effects[][2] = { 572 { V4L2_COLORFX_NONE, ISP_IMAGE_EFFECT_DISABLE }, 573 { V4L2_COLORFX_BW, ISP_IMAGE_EFFECT_MONOCHROME }, 574 { V4L2_COLORFX_SEPIA, ISP_IMAGE_EFFECT_SEPIA }, 575 { V4L2_COLORFX_NEGATIVE, ISP_IMAGE_EFFECT_NEGATIVE_MONO }, 576 { 16 /* TODO */, ISP_IMAGE_EFFECT_NEGATIVE_COLOR }, 577 }; 578 int i; 579 580 for (i = 0; i < ARRAY_SIZE(effects); i++) { 581 if (effects[i][0] != value) 582 continue; 583 584 __is_set_isp_effect(is, effects[i][1]); 585 return 0; 586 } 587 588 return -EINVAL; 589} 590 591static int fimc_is_s_ctrl(struct v4l2_ctrl *ctrl) 592{ 593 struct fimc_isp *isp = ctrl_to_fimc_isp(ctrl); 594 struct fimc_is *is = fimc_isp_to_is(isp); 595 bool set_param = true; 596 int ret = 0; 597 598 switch (ctrl->id) { 599 case V4L2_CID_CONTRAST: 600 __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_CONTRAST, 601 ctrl->val); 602 break; 603 604 case V4L2_CID_SATURATION: 605 __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_SATURATION, 606 ctrl->val); 607 break; 608 609 case V4L2_CID_SHARPNESS: 610 __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_SHARPNESS, 611 ctrl->val); 612 break; 613 614 case V4L2_CID_EXPOSURE_ABSOLUTE: 615 __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_EXPOSURE, 616 ctrl->val); 617 break; 618 619 case V4L2_CID_BRIGHTNESS: 620 __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_BRIGHTNESS, 621 ctrl->val); 622 break; 623 624 case V4L2_CID_HUE: 625 __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_HUE, 626 ctrl->val); 627 break; 628 629 case V4L2_CID_EXPOSURE_METERING: 630 ret = __ctrl_set_metering(is, ctrl->val); 631 break; 632 633 case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: 634 ret = __ctrl_set_white_balance(is, ctrl->val); 635 break; 636 637 case V4L2_CID_3A_LOCK: 638 ret = __ctrl_set_aewb_lock(is, ctrl); 639 set_param = false; 640 break; 641 642 case V4L2_CID_ISO_SENSITIVITY_AUTO: 643 ret = __ctrl_set_iso(is, ctrl->val); 644 break; 645 646 case V4L2_CID_POWER_LINE_FREQUENCY: 647 ret = __ctrl_set_afc(is, ctrl->val); 648 break; 649 650 case V4L2_CID_COLORFX: 651 __ctrl_set_image_effect(is, ctrl->val); 652 break; 653 654 default: 655 ret = -EINVAL; 656 break; 657 } 658 659 if (ret < 0) { 660 v4l2_err(&isp->subdev, "Failed to set control: %s (%d)\n", 661 ctrl->name, ctrl->val); 662 return ret; 663 } 664 665 if (set_param && test_bit(IS_ST_STREAM_ON, &is->state)) 666 return fimc_is_itf_s_param(is, true); 667 668 return 0; 669} 670 671static const struct v4l2_ctrl_ops fimc_isp_ctrl_ops = { 672 .s_ctrl = fimc_is_s_ctrl, 673}; 674 675static void __isp_subdev_set_default_format(struct fimc_isp *isp) 676{ 677 struct fimc_is *is = fimc_isp_to_is(isp); 678 679 isp->sink_fmt.width = DEFAULT_PREVIEW_STILL_WIDTH + 680 FIMC_ISP_CAC_MARGIN_WIDTH; 681 isp->sink_fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT + 682 FIMC_ISP_CAC_MARGIN_HEIGHT; 683 isp->sink_fmt.code = MEDIA_BUS_FMT_SGRBG10_1X10; 684 685 isp->src_fmt.width = DEFAULT_PREVIEW_STILL_WIDTH; 686 isp->src_fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT; 687 isp->src_fmt.code = MEDIA_BUS_FMT_SGRBG10_1X10; 688 __is_set_frame_size(is, &isp->src_fmt); 689} 690 691int fimc_isp_subdev_create(struct fimc_isp *isp) 692{ 693 const struct v4l2_ctrl_ops *ops = &fimc_isp_ctrl_ops; 694 struct v4l2_ctrl_handler *handler = &isp->ctrls.handler; 695 struct v4l2_subdev *sd = &isp->subdev; 696 struct fimc_isp_ctrls *ctrls = &isp->ctrls; 697 int ret; 698 699 mutex_init(&isp->subdev_lock); 700 701 v4l2_subdev_init(sd, &fimc_is_subdev_ops); 702 703 sd->owner = THIS_MODULE; 704 sd->grp_id = GRP_ID_FIMC_IS; 705 sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 706 snprintf(sd->name, sizeof(sd->name), "FIMC-IS-ISP"); 707 708 isp->subdev_pads[FIMC_ISP_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; 709 isp->subdev_pads[FIMC_ISP_SD_PAD_SRC_FIFO].flags = MEDIA_PAD_FL_SOURCE; 710 isp->subdev_pads[FIMC_ISP_SD_PAD_SRC_DMA].flags = MEDIA_PAD_FL_SOURCE; 711 ret = media_entity_init(&sd->entity, FIMC_ISP_SD_PADS_NUM, 712 isp->subdev_pads, 0); 713 if (ret) 714 return ret; 715 716 v4l2_ctrl_handler_init(handler, 20); 717 718 ctrls->saturation = v4l2_ctrl_new_std(handler, ops, V4L2_CID_SATURATION, 719 -2, 2, 1, 0); 720 ctrls->brightness = v4l2_ctrl_new_std(handler, ops, V4L2_CID_BRIGHTNESS, 721 -4, 4, 1, 0); 722 ctrls->contrast = v4l2_ctrl_new_std(handler, ops, V4L2_CID_CONTRAST, 723 -2, 2, 1, 0); 724 ctrls->sharpness = v4l2_ctrl_new_std(handler, ops, V4L2_CID_SHARPNESS, 725 -2, 2, 1, 0); 726 ctrls->hue = v4l2_ctrl_new_std(handler, ops, V4L2_CID_HUE, 727 -2, 2, 1, 0); 728 729 ctrls->auto_wb = v4l2_ctrl_new_std_menu(handler, ops, 730 V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, 731 8, ~0x14e, V4L2_WHITE_BALANCE_AUTO); 732 733 ctrls->exposure = v4l2_ctrl_new_std(handler, ops, 734 V4L2_CID_EXPOSURE_ABSOLUTE, 735 -4, 4, 1, 0); 736 737 ctrls->exp_metering = v4l2_ctrl_new_std_menu(handler, ops, 738 V4L2_CID_EXPOSURE_METERING, 3, 739 ~0xf, V4L2_EXPOSURE_METERING_AVERAGE); 740 741 v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_POWER_LINE_FREQUENCY, 742 V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, 743 V4L2_CID_POWER_LINE_FREQUENCY_AUTO); 744 /* ISO sensitivity */ 745 ctrls->auto_iso = v4l2_ctrl_new_std_menu(handler, ops, 746 V4L2_CID_ISO_SENSITIVITY_AUTO, 1, 0, 747 V4L2_ISO_SENSITIVITY_AUTO); 748 749 ctrls->iso = v4l2_ctrl_new_int_menu(handler, ops, 750 V4L2_CID_ISO_SENSITIVITY, ARRAY_SIZE(iso_qmenu) - 1, 751 ARRAY_SIZE(iso_qmenu)/2 - 1, iso_qmenu); 752 753 ctrls->aewb_lock = v4l2_ctrl_new_std(handler, ops, 754 V4L2_CID_3A_LOCK, 0, 0x3, 0, 0); 755 756 /* TODO: Add support for NEGATIVE_COLOR option */ 757 ctrls->colorfx = v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_COLORFX, 758 V4L2_COLORFX_SET_CBCR + 1, ~0x1000f, V4L2_COLORFX_NONE); 759 760 if (handler->error) { 761 media_entity_cleanup(&sd->entity); 762 return handler->error; 763 } 764 765 v4l2_ctrl_auto_cluster(2, &ctrls->auto_iso, 766 V4L2_ISO_SENSITIVITY_MANUAL, false); 767 768 sd->ctrl_handler = handler; 769 sd->internal_ops = &fimc_is_subdev_internal_ops; 770 sd->entity.ops = &fimc_is_subdev_media_ops; 771 v4l2_set_subdevdata(sd, isp); 772 773 __isp_subdev_set_default_format(isp); 774 775 return 0; 776} 777 778void fimc_isp_subdev_destroy(struct fimc_isp *isp) 779{ 780 struct v4l2_subdev *sd = &isp->subdev; 781 782 v4l2_device_unregister_subdev(sd); 783 media_entity_cleanup(&sd->entity); 784 v4l2_ctrl_handler_free(&isp->ctrls.handler); 785 v4l2_set_subdevdata(sd, NULL); 786} 787