Commit | Line | Data |
---|---|---|
9a761e43 SN |
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" | |
34947b8a | 28 | #include "fimc-isp-video.h" |
9a761e43 SN |
29 | #include "fimc-is-command.h" |
30 | #include "fimc-is-param.h" | |
31 | #include "fimc-is-regs.h" | |
32 | #include "fimc-is.h" | |
33 | ||
4434adff SN |
34 | int fimc_isp_debug; |
35 | module_param_named(debug_isp, fimc_isp_debug, int, S_IRUGO | S_IWUSR); | |
9a761e43 SN |
36 | |
37 | static 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, | |
27ffaeb0 | 44 | .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, |
9a761e43 SN |
45 | }, { |
46 | .name = "RAW10 (GRBG)", | |
47 | .fourcc = V4L2_PIX_FMT_SGRBG10, | |
48 | .depth = { 10 }, | |
49 | .color = FIMC_FMT_RAW10, | |
50 | .memplanes = 1, | |
27ffaeb0 | 51 | .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, |
9a761e43 SN |
52 | }, { |
53 | .name = "RAW12 (GRBG)", | |
54 | .fourcc = V4L2_PIX_FMT_SGRBG12, | |
55 | .depth = { 12 }, | |
56 | .color = FIMC_FMT_RAW12, | |
57 | .memplanes = 1, | |
27ffaeb0 | 58 | .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, |
9a761e43 SN |
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 | */ | |
68 | const 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 | ||
91 | void 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); | |
34947b8a | 97 | fimc_isp_video_irq_handler(is); |
9a761e43 | 98 | |
9a761e43 SN |
99 | wake_up(&is->irq_queue); |
100 | } | |
101 | ||
102 | /* Capture subdev media entity operations */ | |
103 | static 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 | ||
110 | static const struct media_entity_operations fimc_is_subdev_media_ops = { | |
111 | .link_setup = fimc_is_link_setup, | |
112 | }; | |
113 | ||
114 | static int fimc_is_subdev_enum_mbus_code(struct v4l2_subdev *sd, | |
f7234138 | 115 | struct v4l2_subdev_pad_config *cfg, |
9a761e43 SN |
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 | ||
127 | static int fimc_isp_subdev_get_fmt(struct v4l2_subdev *sd, | |
f7234138 | 128 | struct v4l2_subdev_pad_config *cfg, |
9a761e43 SN |
129 | struct v4l2_subdev_format *fmt) |
130 | { | |
131 | struct fimc_isp *isp = v4l2_get_subdevdata(sd); | |
9a761e43 | 132 | struct v4l2_mbus_framefmt *mf = &fmt->format; |
9a761e43 SN |
133 | |
134 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { | |
f7234138 | 135 | *mf = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); |
9a761e43 SN |
136 | return 0; |
137 | } | |
138 | ||
ac2a4a86 | 139 | mf->colorspace = V4L2_COLORSPACE_SRGB; |
9a761e43 SN |
140 | |
141 | mutex_lock(&isp->subdev_lock); | |
9a761e43 SN |
142 | |
143 | if (fmt->pad == FIMC_ISP_SD_PAD_SINK) { | |
5cfaad64 SN |
144 | /* ISP OTF input image format */ |
145 | *mf = isp->sink_fmt; | |
9a761e43 | 146 | } else { |
5cfaad64 SN |
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; | |
27ffaeb0 | 152 | mf->code = MEDIA_BUS_FMT_YUV10_1X30; |
5cfaad64 | 153 | } |
9a761e43 SN |
154 | } |
155 | ||
156 | mutex_unlock(&isp->subdev_lock); | |
157 | ||
4434adff SN |
158 | isp_dbg(1, sd, "%s: pad%d: fmt: 0x%x, %dx%d\n", __func__, |
159 | fmt->pad, mf->code, mf->width, mf->height); | |
9a761e43 SN |
160 | |
161 | return 0; | |
162 | } | |
163 | ||
164 | static void __isp_subdev_try_format(struct fimc_isp *isp, | |
f7234138 | 165 | struct v4l2_subdev_pad_config *cfg, |
5cfaad64 | 166 | struct v4l2_subdev_format *fmt) |
9a761e43 SN |
167 | { |
168 | struct v4l2_mbus_framefmt *mf = &fmt->format; | |
5cfaad64 SN |
169 | struct v4l2_mbus_framefmt *format; |
170 | ||
171 | mf->colorspace = V4L2_COLORSPACE_SRGB; | |
9a761e43 SN |
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); | |
27ffaeb0 | 178 | mf->code = MEDIA_BUS_FMT_SGRBG10_1X10; |
9a761e43 | 179 | } else { |
5cfaad64 | 180 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) |
f7234138 | 181 | format = v4l2_subdev_get_try_format(&isp->subdev, cfg, |
5cfaad64 SN |
182 | FIMC_ISP_SD_PAD_SINK); |
183 | else | |
184 | format = &isp->sink_fmt; | |
185 | ||
9a761e43 | 186 | /* Allow changing format only on sink pad */ |
5cfaad64 SN |
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) { | |
27ffaeb0 | 191 | mf->code = MEDIA_BUS_FMT_YUV10_1X30; |
5cfaad64 SN |
192 | mf->colorspace = V4L2_COLORSPACE_JPEG; |
193 | } else { | |
194 | mf->code = format->code; | |
195 | } | |
9a761e43 SN |
196 | } |
197 | } | |
198 | ||
199 | static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd, | |
f7234138 | 200 | struct v4l2_subdev_pad_config *cfg, |
9a761e43 SN |
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 | ||
4434adff | 208 | isp_dbg(1, sd, "%s: pad%d: code: 0x%x, %dx%d\n", |
9a761e43 SN |
209 | __func__, fmt->pad, mf->code, mf->width, mf->height); |
210 | ||
9a761e43 | 211 | mutex_lock(&isp->subdev_lock); |
f7234138 | 212 | __isp_subdev_try_format(isp, cfg, fmt); |
9a761e43 SN |
213 | |
214 | if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { | |
f7234138 | 215 | mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); |
9a761e43 | 216 | *mf = fmt->format; |
5cfaad64 SN |
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; | |
f7234138 HV |
226 | __isp_subdev_try_format(isp, cfg, &format); |
227 | mf = v4l2_subdev_get_try_format(sd, cfg, pad); | |
5cfaad64 SN |
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; | |
f7234138 | 239 | __isp_subdev_try_format(isp, cfg, &format); |
5cfaad64 SN |
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 | } | |
9a761e43 SN |
249 | } |
250 | ||
9a761e43 | 251 | mutex_unlock(&isp->subdev_lock); |
9a761e43 SN |
252 | return ret; |
253 | } | |
254 | ||
255 | static 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 | ||
4434adff | 261 | isp_dbg(1, sd, "%s: on: %d\n", __func__, on); |
9a761e43 SN |
262 | |
263 | if (!test_bit(IS_ST_INIT_DONE, &is->state)) | |
264 | return -EBUSY; | |
265 | ||
266 | fimc_is_mem_barrier(); | |
267 | ||
268 | if (on) { | |
a6f5635e | 269 | if (__get_pending_param_count(is)) { |
9a761e43 | 270 | ret = fimc_is_itf_s_param(is, true); |
a6f5635e SN |
271 | if (ret < 0) |
272 | return ret; | |
273 | } | |
9a761e43 | 274 | |
4434adff SN |
275 | isp_dbg(1, sd, "changing mode to %d\n", is->config_index); |
276 | ||
9a761e43 SN |
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 | ||
304 | static 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; | |
0e761b21 PC |
357 | is->config[is->config_index].p_region_index[0] = 0; |
358 | is->config[is->config_index].p_region_index[1] = 0; | |
9a761e43 SN |
359 | set_bit(IS_ST_IDLE, &is->state); |
360 | wmb(); | |
361 | } | |
362 | ||
363 | return ret; | |
364 | } | |
365 | ||
366 | static 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 | ||
f7234138 | 372 | format = v4l2_subdev_get_try_format(sd, fh->pad, FIMC_ISP_SD_PAD_SINK); |
9a761e43 SN |
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 | ||
f7234138 | 381 | format = v4l2_subdev_get_try_format(sd, fh->pad, FIMC_ISP_SD_PAD_SRC_FIFO); |
9a761e43 SN |
382 | fmt.width = DEFAULT_PREVIEW_STILL_WIDTH; |
383 | fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT; | |
384 | *format = fmt; | |
385 | ||
f7234138 | 386 | format = v4l2_subdev_get_try_format(sd, fh->pad, FIMC_ISP_SD_PAD_SRC_DMA); |
9a761e43 SN |
387 | *format = fmt; |
388 | ||
389 | return 0; | |
390 | } | |
391 | ||
34947b8a SN |
392 | static 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 | ||
408 | static 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 | ||
9a761e43 | 416 | static const struct v4l2_subdev_internal_ops fimc_is_subdev_internal_ops = { |
34947b8a SN |
417 | .registered = fimc_isp_subdev_registered, |
418 | .unregistered = fimc_isp_subdev_unregistered, | |
9a761e43 SN |
419 | .open = fimc_isp_subdev_open, |
420 | }; | |
421 | ||
422 | static 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 | ||
428 | static const struct v4l2_subdev_video_ops fimc_is_subdev_video_ops = { | |
429 | .s_stream = fimc_isp_subdev_s_stream, | |
430 | }; | |
431 | ||
432 | static const struct v4l2_subdev_core_ops fimc_is_core_ops = { | |
433 | .s_power = fimc_isp_subdev_s_power, | |
434 | }; | |
435 | ||
436 | static 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 | ||
442 | static 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 | ||
471 | static 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); | |
9a761e43 SN |
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); | |
9a761e43 SN |
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 */ | |
501 | static const s64 iso_qmenu[] = { | |
502 | 50, 100, 200, 400, 800, | |
503 | }; | |
504 | ||
505 | static 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 | ||
522 | static 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; | |
2028c71d | 541 | } |
9a761e43 SN |
542 | |
543 | __is_set_isp_metering(is, IS_METERING_CONFIG_CMD, val); | |
544 | return 0; | |
545 | } | |
546 | ||
547 | static 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 | ||
569 | static 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 | ||
591 | static 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 | ||
671 | static const struct v4l2_ctrl_ops fimc_isp_ctrl_ops = { | |
672 | .s_ctrl = fimc_is_s_ctrl, | |
673 | }; | |
674 | ||
94945730 SN |
675 | static 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; | |
27ffaeb0 | 683 | isp->sink_fmt.code = MEDIA_BUS_FMT_SGRBG10_1X10; |
94945730 SN |
684 | |
685 | isp->src_fmt.width = DEFAULT_PREVIEW_STILL_WIDTH; | |
686 | isp->src_fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT; | |
27ffaeb0 | 687 | isp->src_fmt.code = MEDIA_BUS_FMT_SGRBG10_1X10; |
94945730 SN |
688 | __is_set_frame_size(is, &isp->src_fmt); |
689 | } | |
690 | ||
9a761e43 SN |
691 | int 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); | |
c1cd2b96 SN |
702 | |
703 | sd->owner = THIS_MODULE; | |
9a761e43 | 704 | sd->grp_id = GRP_ID_FIMC_IS; |
5a66561f | 705 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; |
9a761e43 SN |
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; | |
ab22e77c | 711 | ret = media_entity_pads_init(&sd->entity, FIMC_ISP_SD_PADS_NUM, |
18095107 | 712 | isp->subdev_pads); |
9a761e43 SN |
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 | ||
94945730 SN |
773 | __isp_subdev_set_default_format(isp); |
774 | ||
9a761e43 SN |
775 | return 0; |
776 | } | |
777 | ||
778 | void 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 | } |