diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/media/video/davinci/dm355_ccdc.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/media/video/davinci/dm355_ccdc.c')
-rw-r--r-- | drivers/media/video/davinci/dm355_ccdc.c | 1082 |
1 files changed, 1082 insertions, 0 deletions
diff --git a/drivers/media/video/davinci/dm355_ccdc.c b/drivers/media/video/davinci/dm355_ccdc.c new file mode 100644 index 00000000000..c29ac88ffd7 --- /dev/null +++ b/drivers/media/video/davinci/dm355_ccdc.c | |||
@@ -0,0 +1,1082 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2005-2009 Texas Instruments Inc | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
17 | * | ||
18 | * CCDC hardware module for DM355 | ||
19 | * ------------------------------ | ||
20 | * | ||
21 | * This module is for configuring DM355 CCD controller of VPFE to capture | ||
22 | * Raw yuv or Bayer RGB data from a decoder. CCDC has several modules | ||
23 | * such as Defect Pixel Correction, Color Space Conversion etc to | ||
24 | * pre-process the Bayer RGB data, before writing it to SDRAM. This | ||
25 | * module also allows application to configure individual | ||
26 | * module parameters through VPFE_CMD_S_CCDC_RAW_PARAMS IOCTL. | ||
27 | * To do so, application include dm355_ccdc.h and vpfe_capture.h header | ||
28 | * files. The setparams() API is called by vpfe_capture driver | ||
29 | * to configure module parameters | ||
30 | * | ||
31 | * TODO: 1) Raw bayer parameter settings and bayer capture | ||
32 | * 2) Split module parameter structure to module specific ioctl structs | ||
33 | * 3) add support for lense shading correction | ||
34 | * 4) investigate if enum used for user space type definition | ||
35 | * to be replaced by #defines or integer | ||
36 | */ | ||
37 | #include <linux/platform_device.h> | ||
38 | #include <linux/uaccess.h> | ||
39 | #include <linux/videodev2.h> | ||
40 | #include <linux/clk.h> | ||
41 | #include <linux/err.h> | ||
42 | |||
43 | #include <media/davinci/dm355_ccdc.h> | ||
44 | #include <media/davinci/vpss.h> | ||
45 | |||
46 | #include "dm355_ccdc_regs.h" | ||
47 | #include "ccdc_hw_device.h" | ||
48 | |||
49 | MODULE_LICENSE("GPL"); | ||
50 | MODULE_DESCRIPTION("CCDC Driver for DM355"); | ||
51 | MODULE_AUTHOR("Texas Instruments"); | ||
52 | |||
53 | static struct ccdc_oper_config { | ||
54 | struct device *dev; | ||
55 | /* CCDC interface type */ | ||
56 | enum vpfe_hw_if_type if_type; | ||
57 | /* Raw Bayer configuration */ | ||
58 | struct ccdc_params_raw bayer; | ||
59 | /* YCbCr configuration */ | ||
60 | struct ccdc_params_ycbcr ycbcr; | ||
61 | /* Master clock */ | ||
62 | struct clk *mclk; | ||
63 | /* slave clock */ | ||
64 | struct clk *sclk; | ||
65 | /* ccdc base address */ | ||
66 | void __iomem *base_addr; | ||
67 | } ccdc_cfg = { | ||
68 | /* Raw configurations */ | ||
69 | .bayer = { | ||
70 | .pix_fmt = CCDC_PIXFMT_RAW, | ||
71 | .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, | ||
72 | .win = CCDC_WIN_VGA, | ||
73 | .fid_pol = VPFE_PINPOL_POSITIVE, | ||
74 | .vd_pol = VPFE_PINPOL_POSITIVE, | ||
75 | .hd_pol = VPFE_PINPOL_POSITIVE, | ||
76 | .gain = { | ||
77 | .r_ye = 256, | ||
78 | .gb_g = 256, | ||
79 | .gr_cy = 256, | ||
80 | .b_mg = 256 | ||
81 | }, | ||
82 | .config_params = { | ||
83 | .datasft = 2, | ||
84 | .mfilt1 = CCDC_NO_MEDIAN_FILTER1, | ||
85 | .mfilt2 = CCDC_NO_MEDIAN_FILTER2, | ||
86 | .alaw = { | ||
87 | .gama_wd = 2, | ||
88 | }, | ||
89 | .blk_clamp = { | ||
90 | .sample_pixel = 1, | ||
91 | .dc_sub = 25 | ||
92 | }, | ||
93 | .col_pat_field0 = { | ||
94 | .olop = CCDC_GREEN_BLUE, | ||
95 | .olep = CCDC_BLUE, | ||
96 | .elop = CCDC_RED, | ||
97 | .elep = CCDC_GREEN_RED | ||
98 | }, | ||
99 | .col_pat_field1 = { | ||
100 | .olop = CCDC_GREEN_BLUE, | ||
101 | .olep = CCDC_BLUE, | ||
102 | .elop = CCDC_RED, | ||
103 | .elep = CCDC_GREEN_RED | ||
104 | }, | ||
105 | }, | ||
106 | }, | ||
107 | /* YCbCr configuration */ | ||
108 | .ycbcr = { | ||
109 | .win = CCDC_WIN_PAL, | ||
110 | .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, | ||
111 | .frm_fmt = CCDC_FRMFMT_INTERLACED, | ||
112 | .fid_pol = VPFE_PINPOL_POSITIVE, | ||
113 | .vd_pol = VPFE_PINPOL_POSITIVE, | ||
114 | .hd_pol = VPFE_PINPOL_POSITIVE, | ||
115 | .bt656_enable = 1, | ||
116 | .pix_order = CCDC_PIXORDER_CBYCRY, | ||
117 | .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED | ||
118 | }, | ||
119 | }; | ||
120 | |||
121 | |||
122 | /* Raw Bayer formats */ | ||
123 | static u32 ccdc_raw_bayer_pix_formats[] = | ||
124 | {V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16}; | ||
125 | |||
126 | /* Raw YUV formats */ | ||
127 | static u32 ccdc_raw_yuv_pix_formats[] = | ||
128 | {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV}; | ||
129 | |||
130 | /* register access routines */ | ||
131 | static inline u32 regr(u32 offset) | ||
132 | { | ||
133 | return __raw_readl(ccdc_cfg.base_addr + offset); | ||
134 | } | ||
135 | |||
136 | static inline void regw(u32 val, u32 offset) | ||
137 | { | ||
138 | __raw_writel(val, ccdc_cfg.base_addr + offset); | ||
139 | } | ||
140 | |||
141 | static void ccdc_enable(int en) | ||
142 | { | ||
143 | unsigned int temp; | ||
144 | temp = regr(SYNCEN); | ||
145 | temp &= (~CCDC_SYNCEN_VDHDEN_MASK); | ||
146 | temp |= (en & CCDC_SYNCEN_VDHDEN_MASK); | ||
147 | regw(temp, SYNCEN); | ||
148 | } | ||
149 | |||
150 | static void ccdc_enable_output_to_sdram(int en) | ||
151 | { | ||
152 | unsigned int temp; | ||
153 | temp = regr(SYNCEN); | ||
154 | temp &= (~(CCDC_SYNCEN_WEN_MASK)); | ||
155 | temp |= ((en << CCDC_SYNCEN_WEN_SHIFT) & CCDC_SYNCEN_WEN_MASK); | ||
156 | regw(temp, SYNCEN); | ||
157 | } | ||
158 | |||
159 | static void ccdc_config_gain_offset(void) | ||
160 | { | ||
161 | /* configure gain */ | ||
162 | regw(ccdc_cfg.bayer.gain.r_ye, RYEGAIN); | ||
163 | regw(ccdc_cfg.bayer.gain.gr_cy, GRCYGAIN); | ||
164 | regw(ccdc_cfg.bayer.gain.gb_g, GBGGAIN); | ||
165 | regw(ccdc_cfg.bayer.gain.b_mg, BMGGAIN); | ||
166 | /* configure offset */ | ||
167 | regw(ccdc_cfg.bayer.ccdc_offset, OFFSET); | ||
168 | } | ||
169 | |||
170 | /* | ||
171 | * ccdc_restore_defaults() | ||
172 | * This function restore power on defaults in the ccdc registers | ||
173 | */ | ||
174 | static int ccdc_restore_defaults(void) | ||
175 | { | ||
176 | int i; | ||
177 | |||
178 | dev_dbg(ccdc_cfg.dev, "\nstarting ccdc_restore_defaults..."); | ||
179 | /* set all registers to zero */ | ||
180 | for (i = 0; i <= CCDC_REG_LAST; i += 4) | ||
181 | regw(0, i); | ||
182 | |||
183 | /* now override the values with power on defaults in registers */ | ||
184 | regw(MODESET_DEFAULT, MODESET); | ||
185 | /* no culling support */ | ||
186 | regw(CULH_DEFAULT, CULH); | ||
187 | regw(CULV_DEFAULT, CULV); | ||
188 | /* Set default Gain and Offset */ | ||
189 | ccdc_cfg.bayer.gain.r_ye = GAIN_DEFAULT; | ||
190 | ccdc_cfg.bayer.gain.gb_g = GAIN_DEFAULT; | ||
191 | ccdc_cfg.bayer.gain.gr_cy = GAIN_DEFAULT; | ||
192 | ccdc_cfg.bayer.gain.b_mg = GAIN_DEFAULT; | ||
193 | ccdc_config_gain_offset(); | ||
194 | regw(OUTCLIP_DEFAULT, OUTCLIP); | ||
195 | regw(LSCCFG2_DEFAULT, LSCCFG2); | ||
196 | /* select ccdc input */ | ||
197 | if (vpss_select_ccdc_source(VPSS_CCDCIN)) { | ||
198 | dev_dbg(ccdc_cfg.dev, "\ncouldn't select ccdc input source"); | ||
199 | return -EFAULT; | ||
200 | } | ||
201 | /* select ccdc clock */ | ||
202 | if (vpss_enable_clock(VPSS_CCDC_CLOCK, 1) < 0) { | ||
203 | dev_dbg(ccdc_cfg.dev, "\ncouldn't enable ccdc clock"); | ||
204 | return -EFAULT; | ||
205 | } | ||
206 | dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_restore_defaults..."); | ||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | static int ccdc_open(struct device *device) | ||
211 | { | ||
212 | return ccdc_restore_defaults(); | ||
213 | } | ||
214 | |||
215 | static int ccdc_close(struct device *device) | ||
216 | { | ||
217 | /* disable clock */ | ||
218 | vpss_enable_clock(VPSS_CCDC_CLOCK, 0); | ||
219 | /* do nothing for now */ | ||
220 | return 0; | ||
221 | } | ||
222 | /* | ||
223 | * ccdc_setwin() | ||
224 | * This function will configure the window size to | ||
225 | * be capture in CCDC reg. | ||
226 | */ | ||
227 | static void ccdc_setwin(struct v4l2_rect *image_win, | ||
228 | enum ccdc_frmfmt frm_fmt, int ppc) | ||
229 | { | ||
230 | int horz_start, horz_nr_pixels; | ||
231 | int vert_start, vert_nr_lines; | ||
232 | int mid_img = 0; | ||
233 | |||
234 | dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_setwin..."); | ||
235 | |||
236 | /* | ||
237 | * ppc - per pixel count. indicates how many pixels per cell | ||
238 | * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. | ||
239 | * raw capture this is 1 | ||
240 | */ | ||
241 | horz_start = image_win->left << (ppc - 1); | ||
242 | horz_nr_pixels = ((image_win->width) << (ppc - 1)) - 1; | ||
243 | |||
244 | /* Writing the horizontal info into the registers */ | ||
245 | regw(horz_start, SPH); | ||
246 | regw(horz_nr_pixels, NPH); | ||
247 | vert_start = image_win->top; | ||
248 | |||
249 | if (frm_fmt == CCDC_FRMFMT_INTERLACED) { | ||
250 | vert_nr_lines = (image_win->height >> 1) - 1; | ||
251 | vert_start >>= 1; | ||
252 | /* Since first line doesn't have any data */ | ||
253 | vert_start += 1; | ||
254 | /* configure VDINT0 and VDINT1 */ | ||
255 | regw(vert_start, VDINT0); | ||
256 | } else { | ||
257 | /* Since first line doesn't have any data */ | ||
258 | vert_start += 1; | ||
259 | vert_nr_lines = image_win->height - 1; | ||
260 | /* configure VDINT0 and VDINT1 */ | ||
261 | mid_img = vert_start + (image_win->height / 2); | ||
262 | regw(vert_start, VDINT0); | ||
263 | regw(mid_img, VDINT1); | ||
264 | } | ||
265 | regw(vert_start & CCDC_START_VER_ONE_MASK, SLV0); | ||
266 | regw(vert_start & CCDC_START_VER_TWO_MASK, SLV1); | ||
267 | regw(vert_nr_lines & CCDC_NUM_LINES_VER, NLV); | ||
268 | dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_setwin..."); | ||
269 | } | ||
270 | |||
271 | static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam) | ||
272 | { | ||
273 | if (ccdcparam->datasft < CCDC_DATA_NO_SHIFT || | ||
274 | ccdcparam->datasft > CCDC_DATA_SHIFT_6BIT) { | ||
275 | dev_dbg(ccdc_cfg.dev, "Invalid value of data shift\n"); | ||
276 | return -EINVAL; | ||
277 | } | ||
278 | |||
279 | if (ccdcparam->mfilt1 < CCDC_NO_MEDIAN_FILTER1 || | ||
280 | ccdcparam->mfilt1 > CCDC_MEDIAN_FILTER1) { | ||
281 | dev_dbg(ccdc_cfg.dev, "Invalid value of median filter1\n"); | ||
282 | return -EINVAL; | ||
283 | } | ||
284 | |||
285 | if (ccdcparam->mfilt2 < CCDC_NO_MEDIAN_FILTER2 || | ||
286 | ccdcparam->mfilt2 > CCDC_MEDIAN_FILTER2) { | ||
287 | dev_dbg(ccdc_cfg.dev, "Invalid value of median filter2\n"); | ||
288 | return -EINVAL; | ||
289 | } | ||
290 | |||
291 | if ((ccdcparam->med_filt_thres < 0) || | ||
292 | (ccdcparam->med_filt_thres > CCDC_MED_FILT_THRESH)) { | ||
293 | dev_dbg(ccdc_cfg.dev, | ||
294 | "Invalid value of median filter thresold\n"); | ||
295 | return -EINVAL; | ||
296 | } | ||
297 | |||
298 | if (ccdcparam->data_sz < CCDC_DATA_16BITS || | ||
299 | ccdcparam->data_sz > CCDC_DATA_8BITS) { | ||
300 | dev_dbg(ccdc_cfg.dev, "Invalid value of data size\n"); | ||
301 | return -EINVAL; | ||
302 | } | ||
303 | |||
304 | if (ccdcparam->alaw.enable) { | ||
305 | if (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_13_4 || | ||
306 | ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) { | ||
307 | dev_dbg(ccdc_cfg.dev, "Invalid value of ALAW\n"); | ||
308 | return -EINVAL; | ||
309 | } | ||
310 | } | ||
311 | |||
312 | if (ccdcparam->blk_clamp.b_clamp_enable) { | ||
313 | if (ccdcparam->blk_clamp.sample_pixel < CCDC_SAMPLE_1PIXELS || | ||
314 | ccdcparam->blk_clamp.sample_pixel > CCDC_SAMPLE_16PIXELS) { | ||
315 | dev_dbg(ccdc_cfg.dev, | ||
316 | "Invalid value of sample pixel\n"); | ||
317 | return -EINVAL; | ||
318 | } | ||
319 | if (ccdcparam->blk_clamp.sample_ln < CCDC_SAMPLE_1LINES || | ||
320 | ccdcparam->blk_clamp.sample_ln > CCDC_SAMPLE_16LINES) { | ||
321 | dev_dbg(ccdc_cfg.dev, | ||
322 | "Invalid value of sample lines\n"); | ||
323 | return -EINVAL; | ||
324 | } | ||
325 | } | ||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | /* Parameter operations */ | ||
330 | static int ccdc_set_params(void __user *params) | ||
331 | { | ||
332 | struct ccdc_config_params_raw ccdc_raw_params; | ||
333 | int x; | ||
334 | |||
335 | /* only raw module parameters can be set through the IOCTL */ | ||
336 | if (ccdc_cfg.if_type != VPFE_RAW_BAYER) | ||
337 | return -EINVAL; | ||
338 | |||
339 | x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params)); | ||
340 | if (x) { | ||
341 | dev_dbg(ccdc_cfg.dev, "ccdc_set_params: error in copying ccdc" | ||
342 | "params, %d\n", x); | ||
343 | return -EFAULT; | ||
344 | } | ||
345 | |||
346 | if (!validate_ccdc_param(&ccdc_raw_params)) { | ||
347 | memcpy(&ccdc_cfg.bayer.config_params, | ||
348 | &ccdc_raw_params, | ||
349 | sizeof(ccdc_raw_params)); | ||
350 | return 0; | ||
351 | } | ||
352 | return -EINVAL; | ||
353 | } | ||
354 | |||
355 | /* This function will configure CCDC for YCbCr video capture */ | ||
356 | static void ccdc_config_ycbcr(void) | ||
357 | { | ||
358 | struct ccdc_params_ycbcr *params = &ccdc_cfg.ycbcr; | ||
359 | u32 temp; | ||
360 | |||
361 | /* first set the CCDC power on defaults values in all registers */ | ||
362 | dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_ycbcr..."); | ||
363 | ccdc_restore_defaults(); | ||
364 | |||
365 | /* configure pixel format & video frame format */ | ||
366 | temp = (((params->pix_fmt & CCDC_INPUT_MODE_MASK) << | ||
367 | CCDC_INPUT_MODE_SHIFT) | | ||
368 | ((params->frm_fmt & CCDC_FRM_FMT_MASK) << | ||
369 | CCDC_FRM_FMT_SHIFT)); | ||
370 | |||
371 | /* setup BT.656 sync mode */ | ||
372 | if (params->bt656_enable) { | ||
373 | regw(CCDC_REC656IF_BT656_EN, REC656IF); | ||
374 | /* | ||
375 | * configure the FID, VD, HD pin polarity fld,hd pol positive, | ||
376 | * vd negative, 8-bit pack mode | ||
377 | */ | ||
378 | temp |= CCDC_VD_POL_NEGATIVE; | ||
379 | } else { /* y/c external sync mode */ | ||
380 | temp |= (((params->fid_pol & CCDC_FID_POL_MASK) << | ||
381 | CCDC_FID_POL_SHIFT) | | ||
382 | ((params->hd_pol & CCDC_HD_POL_MASK) << | ||
383 | CCDC_HD_POL_SHIFT) | | ||
384 | ((params->vd_pol & CCDC_VD_POL_MASK) << | ||
385 | CCDC_VD_POL_SHIFT)); | ||
386 | } | ||
387 | |||
388 | /* pack the data to 8-bit */ | ||
389 | temp |= CCDC_DATA_PACK_ENABLE; | ||
390 | |||
391 | regw(temp, MODESET); | ||
392 | |||
393 | /* configure video window */ | ||
394 | ccdc_setwin(¶ms->win, params->frm_fmt, 2); | ||
395 | |||
396 | /* configure the order of y cb cr in SD-RAM */ | ||
397 | temp = (params->pix_order << CCDC_Y8POS_SHIFT); | ||
398 | temp |= CCDC_LATCH_ON_VSYNC_DISABLE | CCDC_CCDCFG_FIDMD_NO_LATCH_VSYNC; | ||
399 | regw(temp, CCDCFG); | ||
400 | |||
401 | /* | ||
402 | * configure the horizontal line offset. This is done by rounding up | ||
403 | * width to a multiple of 16 pixels and multiply by two to account for | ||
404 | * y:cb:cr 4:2:2 data | ||
405 | */ | ||
406 | regw(((params->win.width * 2 + 31) >> 5), HSIZE); | ||
407 | |||
408 | /* configure the memory line offset */ | ||
409 | if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) { | ||
410 | /* two fields are interleaved in memory */ | ||
411 | regw(CCDC_SDOFST_FIELD_INTERLEAVED, SDOFST); | ||
412 | } | ||
413 | |||
414 | dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_config_ycbcr...\n"); | ||
415 | } | ||
416 | |||
417 | /* | ||
418 | * ccdc_config_black_clamp() | ||
419 | * configure parameters for Optical Black Clamp | ||
420 | */ | ||
421 | static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp) | ||
422 | { | ||
423 | u32 val; | ||
424 | |||
425 | if (!bclamp->b_clamp_enable) { | ||
426 | /* configure DCSub */ | ||
427 | regw(bclamp->dc_sub & CCDC_BLK_DC_SUB_MASK, DCSUB); | ||
428 | regw(0x0000, CLAMP); | ||
429 | return; | ||
430 | } | ||
431 | /* Enable the Black clamping, set sample lines and pixels */ | ||
432 | val = (bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) | | ||
433 | ((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) << | ||
434 | CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE; | ||
435 | regw(val, CLAMP); | ||
436 | |||
437 | /* If Black clamping is enable then make dcsub 0 */ | ||
438 | val = (bclamp->sample_ln & CCDC_NUM_LINE_CALC_MASK) | ||
439 | << CCDC_NUM_LINE_CALC_SHIFT; | ||
440 | regw(val, DCSUB); | ||
441 | } | ||
442 | |||
443 | /* | ||
444 | * ccdc_config_black_compense() | ||
445 | * configure parameters for Black Compensation | ||
446 | */ | ||
447 | static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp) | ||
448 | { | ||
449 | u32 val; | ||
450 | |||
451 | val = (bcomp->b & CCDC_BLK_COMP_MASK) | | ||
452 | ((bcomp->gb & CCDC_BLK_COMP_MASK) << | ||
453 | CCDC_BLK_COMP_GB_COMP_SHIFT); | ||
454 | regw(val, BLKCMP1); | ||
455 | |||
456 | val = ((bcomp->gr & CCDC_BLK_COMP_MASK) << | ||
457 | CCDC_BLK_COMP_GR_COMP_SHIFT) | | ||
458 | ((bcomp->r & CCDC_BLK_COMP_MASK) << | ||
459 | CCDC_BLK_COMP_R_COMP_SHIFT); | ||
460 | regw(val, BLKCMP0); | ||
461 | } | ||
462 | |||
463 | /* | ||
464 | * ccdc_write_dfc_entry() | ||
465 | * write an entry in the dfc table. | ||
466 | */ | ||
467 | int ccdc_write_dfc_entry(int index, struct ccdc_vertical_dft *dfc) | ||
468 | { | ||
469 | /* TODO This is to be re-visited and adjusted */ | ||
470 | #define DFC_WRITE_WAIT_COUNT 1000 | ||
471 | u32 val, count = DFC_WRITE_WAIT_COUNT; | ||
472 | |||
473 | regw(dfc->dft_corr_vert[index], DFCMEM0); | ||
474 | regw(dfc->dft_corr_horz[index], DFCMEM1); | ||
475 | regw(dfc->dft_corr_sub1[index], DFCMEM2); | ||
476 | regw(dfc->dft_corr_sub2[index], DFCMEM3); | ||
477 | regw(dfc->dft_corr_sub3[index], DFCMEM4); | ||
478 | /* set WR bit to write */ | ||
479 | val = regr(DFCMEMCTL) | CCDC_DFCMEMCTL_DFCMWR_MASK; | ||
480 | regw(val, DFCMEMCTL); | ||
481 | |||
482 | /* | ||
483 | * Assume, it is very short. If we get an error, we need to | ||
484 | * adjust this value | ||
485 | */ | ||
486 | while (regr(DFCMEMCTL) & CCDC_DFCMEMCTL_DFCMWR_MASK) | ||
487 | count--; | ||
488 | /* | ||
489 | * TODO We expect the count to be non-zero to be successful. Adjust | ||
490 | * the count if write requires more time | ||
491 | */ | ||
492 | |||
493 | if (count) { | ||
494 | dev_err(ccdc_cfg.dev, "defect table write timeout !!!\n"); | ||
495 | return -1; | ||
496 | } | ||
497 | return 0; | ||
498 | } | ||
499 | |||
500 | /* | ||
501 | * ccdc_config_vdfc() | ||
502 | * configure parameters for Vertical Defect Correction | ||
503 | */ | ||
504 | static int ccdc_config_vdfc(struct ccdc_vertical_dft *dfc) | ||
505 | { | ||
506 | u32 val; | ||
507 | int i; | ||
508 | |||
509 | /* Configure General Defect Correction. The table used is from IPIPE */ | ||
510 | val = dfc->gen_dft_en & CCDC_DFCCTL_GDFCEN_MASK; | ||
511 | |||
512 | /* Configure Vertical Defect Correction if needed */ | ||
513 | if (!dfc->ver_dft_en) { | ||
514 | /* Enable only General Defect Correction */ | ||
515 | regw(val, DFCCTL); | ||
516 | return 0; | ||
517 | } | ||
518 | |||
519 | if (dfc->table_size > CCDC_DFT_TABLE_SIZE) | ||
520 | return -EINVAL; | ||
521 | |||
522 | val |= CCDC_DFCCTL_VDFC_DISABLE; | ||
523 | val |= (dfc->dft_corr_ctl.vdfcsl & CCDC_DFCCTL_VDFCSL_MASK) << | ||
524 | CCDC_DFCCTL_VDFCSL_SHIFT; | ||
525 | val |= (dfc->dft_corr_ctl.vdfcuda & CCDC_DFCCTL_VDFCUDA_MASK) << | ||
526 | CCDC_DFCCTL_VDFCUDA_SHIFT; | ||
527 | val |= (dfc->dft_corr_ctl.vdflsft & CCDC_DFCCTL_VDFLSFT_MASK) << | ||
528 | CCDC_DFCCTL_VDFLSFT_SHIFT; | ||
529 | regw(val , DFCCTL); | ||
530 | |||
531 | /* clear address ptr to offset 0 */ | ||
532 | val = CCDC_DFCMEMCTL_DFCMARST_MASK << CCDC_DFCMEMCTL_DFCMARST_SHIFT; | ||
533 | |||
534 | /* write defect table entries */ | ||
535 | for (i = 0; i < dfc->table_size; i++) { | ||
536 | /* increment address for non zero index */ | ||
537 | if (i != 0) | ||
538 | val = CCDC_DFCMEMCTL_INC_ADDR; | ||
539 | regw(val, DFCMEMCTL); | ||
540 | if (ccdc_write_dfc_entry(i, dfc) < 0) | ||
541 | return -EFAULT; | ||
542 | } | ||
543 | |||
544 | /* update saturation level and enable dfc */ | ||
545 | regw(dfc->saturation_ctl & CCDC_VDC_DFCVSAT_MASK, DFCVSAT); | ||
546 | val = regr(DFCCTL) | (CCDC_DFCCTL_VDFCEN_MASK << | ||
547 | CCDC_DFCCTL_VDFCEN_SHIFT); | ||
548 | regw(val, DFCCTL); | ||
549 | return 0; | ||
550 | } | ||
551 | |||
552 | /* | ||
553 | * ccdc_config_csc() | ||
554 | * configure parameters for color space conversion | ||
555 | * Each register CSCM0-7 has two values in S8Q5 format. | ||
556 | */ | ||
557 | static void ccdc_config_csc(struct ccdc_csc *csc) | ||
558 | { | ||
559 | u32 val1, val2; | ||
560 | int i; | ||
561 | |||
562 | if (!csc->enable) | ||
563 | return; | ||
564 | |||
565 | /* Enable the CSC sub-module */ | ||
566 | regw(CCDC_CSC_ENABLE, CSCCTL); | ||
567 | |||
568 | /* Converting the co-eff as per the format of the register */ | ||
569 | for (i = 0; i < CCDC_CSC_COEFF_TABLE_SIZE; i++) { | ||
570 | if ((i % 2) == 0) { | ||
571 | /* CSCM - LSB */ | ||
572 | val1 = (csc->coeff[i].integer & | ||
573 | CCDC_CSC_COEF_INTEG_MASK) | ||
574 | << CCDC_CSC_COEF_INTEG_SHIFT; | ||
575 | /* | ||
576 | * convert decimal part to binary. Use 2 decimal | ||
577 | * precision, user values range from .00 - 0.99 | ||
578 | */ | ||
579 | val1 |= (((csc->coeff[i].decimal & | ||
580 | CCDC_CSC_COEF_DECIMAL_MASK) * | ||
581 | CCDC_CSC_DEC_MAX) / 100); | ||
582 | } else { | ||
583 | |||
584 | /* CSCM - MSB */ | ||
585 | val2 = (csc->coeff[i].integer & | ||
586 | CCDC_CSC_COEF_INTEG_MASK) | ||
587 | << CCDC_CSC_COEF_INTEG_SHIFT; | ||
588 | val2 |= (((csc->coeff[i].decimal & | ||
589 | CCDC_CSC_COEF_DECIMAL_MASK) * | ||
590 | CCDC_CSC_DEC_MAX) / 100); | ||
591 | val2 <<= CCDC_CSCM_MSB_SHIFT; | ||
592 | val2 |= val1; | ||
593 | regw(val2, (CSCM0 + ((i - 1) << 1))); | ||
594 | } | ||
595 | } | ||
596 | } | ||
597 | |||
598 | /* | ||
599 | * ccdc_config_color_patterns() | ||
600 | * configure parameters for color patterns | ||
601 | */ | ||
602 | static void ccdc_config_color_patterns(struct ccdc_col_pat *pat0, | ||
603 | struct ccdc_col_pat *pat1) | ||
604 | { | ||
605 | u32 val; | ||
606 | |||
607 | val = (pat0->olop | (pat0->olep << 2) | (pat0->elop << 4) | | ||
608 | (pat0->elep << 6) | (pat1->olop << 8) | (pat1->olep << 10) | | ||
609 | (pat1->elop << 12) | (pat1->elep << 14)); | ||
610 | regw(val, COLPTN); | ||
611 | } | ||
612 | |||
613 | /* This function will configure CCDC for Raw mode image capture */ | ||
614 | static int ccdc_config_raw(void) | ||
615 | { | ||
616 | struct ccdc_params_raw *params = &ccdc_cfg.bayer; | ||
617 | struct ccdc_config_params_raw *config_params = | ||
618 | &ccdc_cfg.bayer.config_params; | ||
619 | unsigned int val; | ||
620 | |||
621 | dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_raw..."); | ||
622 | |||
623 | /* restore power on defaults to register */ | ||
624 | ccdc_restore_defaults(); | ||
625 | |||
626 | /* CCDCFG register: | ||
627 | * set CCD Not to swap input since input is RAW data | ||
628 | * set FID detection function to Latch at V-Sync | ||
629 | * set WENLOG - ccdc valid area to AND | ||
630 | * set TRGSEL to WENBIT | ||
631 | * set EXTRG to DISABLE | ||
632 | * disable latching function on VSYNC - shadowed registers | ||
633 | */ | ||
634 | regw(CCDC_YCINSWP_RAW | CCDC_CCDCFG_FIDMD_LATCH_VSYNC | | ||
635 | CCDC_CCDCFG_WENLOG_AND | CCDC_CCDCFG_TRGSEL_WEN | | ||
636 | CCDC_CCDCFG_EXTRG_DISABLE | CCDC_LATCH_ON_VSYNC_DISABLE, CCDCFG); | ||
637 | |||
638 | /* | ||
639 | * Set VDHD direction to input, input type to raw input | ||
640 | * normal data polarity, do not use external WEN | ||
641 | */ | ||
642 | val = (CCDC_VDHDOUT_INPUT | CCDC_RAW_IP_MODE | CCDC_DATAPOL_NORMAL | | ||
643 | CCDC_EXWEN_DISABLE); | ||
644 | |||
645 | /* | ||
646 | * Configure the vertical sync polarity (MODESET.VDPOL), horizontal | ||
647 | * sync polarity (MODESET.HDPOL), field id polarity (MODESET.FLDPOL), | ||
648 | * frame format(progressive or interlace), & pixel format (Input mode) | ||
649 | */ | ||
650 | val |= (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) | | ||
651 | ((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) | | ||
652 | ((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) | | ||
653 | ((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) | | ||
654 | ((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT)); | ||
655 | |||
656 | /* set pack for alaw compression */ | ||
657 | if ((config_params->data_sz == CCDC_DATA_8BITS) || | ||
658 | config_params->alaw.enable) | ||
659 | val |= CCDC_DATA_PACK_ENABLE; | ||
660 | |||
661 | /* Configure for LPF */ | ||
662 | if (config_params->lpf_enable) | ||
663 | val |= (config_params->lpf_enable & CCDC_LPF_MASK) << | ||
664 | CCDC_LPF_SHIFT; | ||
665 | |||
666 | /* Configure the data shift */ | ||
667 | val |= (config_params->datasft & CCDC_DATASFT_MASK) << | ||
668 | CCDC_DATASFT_SHIFT; | ||
669 | regw(val , MODESET); | ||
670 | dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to MODESET...\n", val); | ||
671 | |||
672 | /* Configure the Median Filter threshold */ | ||
673 | regw((config_params->med_filt_thres) & CCDC_MED_FILT_THRESH, MEDFILT); | ||
674 | |||
675 | /* Configure GAMMAWD register. defaur 11-2, and Mosaic cfa pattern */ | ||
676 | val = CCDC_GAMMA_BITS_11_2 << CCDC_GAMMAWD_INPUT_SHIFT | | ||
677 | CCDC_CFA_MOSAIC; | ||
678 | |||
679 | /* Enable and configure aLaw register if needed */ | ||
680 | if (config_params->alaw.enable) { | ||
681 | val |= (CCDC_ALAW_ENABLE | | ||
682 | ((config_params->alaw.gama_wd & | ||
683 | CCDC_ALAW_GAMA_WD_MASK) << | ||
684 | CCDC_GAMMAWD_INPUT_SHIFT)); | ||
685 | } | ||
686 | |||
687 | /* Configure Median filter1 & filter2 */ | ||
688 | val |= ((config_params->mfilt1 << CCDC_MFILT1_SHIFT) | | ||
689 | (config_params->mfilt2 << CCDC_MFILT2_SHIFT)); | ||
690 | |||
691 | regw(val, GAMMAWD); | ||
692 | dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to GAMMAWD...\n", val); | ||
693 | |||
694 | /* configure video window */ | ||
695 | ccdc_setwin(¶ms->win, params->frm_fmt, 1); | ||
696 | |||
697 | /* Optical Clamp Averaging */ | ||
698 | ccdc_config_black_clamp(&config_params->blk_clamp); | ||
699 | |||
700 | /* Black level compensation */ | ||
701 | ccdc_config_black_compense(&config_params->blk_comp); | ||
702 | |||
703 | /* Vertical Defect Correction if needed */ | ||
704 | if (ccdc_config_vdfc(&config_params->vertical_dft) < 0) | ||
705 | return -EFAULT; | ||
706 | |||
707 | /* color space conversion */ | ||
708 | ccdc_config_csc(&config_params->csc); | ||
709 | |||
710 | /* color pattern */ | ||
711 | ccdc_config_color_patterns(&config_params->col_pat_field0, | ||
712 | &config_params->col_pat_field1); | ||
713 | |||
714 | /* Configure the Gain & offset control */ | ||
715 | ccdc_config_gain_offset(); | ||
716 | |||
717 | dev_dbg(ccdc_cfg.dev, "\nWriting %x to COLPTN...\n", val); | ||
718 | |||
719 | /* Configure DATAOFST register */ | ||
720 | val = (config_params->data_offset.horz_offset & CCDC_DATAOFST_MASK) << | ||
721 | CCDC_DATAOFST_H_SHIFT; | ||
722 | val |= (config_params->data_offset.vert_offset & CCDC_DATAOFST_MASK) << | ||
723 | CCDC_DATAOFST_V_SHIFT; | ||
724 | regw(val, DATAOFST); | ||
725 | |||
726 | /* configuring HSIZE register */ | ||
727 | val = (params->horz_flip_enable & CCDC_HSIZE_FLIP_MASK) << | ||
728 | CCDC_HSIZE_FLIP_SHIFT; | ||
729 | |||
730 | /* If pack 8 is enable then 1 pixel will take 1 byte */ | ||
731 | if ((config_params->data_sz == CCDC_DATA_8BITS) || | ||
732 | config_params->alaw.enable) { | ||
733 | val |= (((params->win.width) + 31) >> 5) & | ||
734 | CCDC_HSIZE_VAL_MASK; | ||
735 | |||
736 | /* adjust to multiple of 32 */ | ||
737 | dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to HSIZE...\n", | ||
738 | (((params->win.width) + 31) >> 5) & | ||
739 | CCDC_HSIZE_VAL_MASK); | ||
740 | } else { | ||
741 | /* else one pixel will take 2 byte */ | ||
742 | val |= (((params->win.width * 2) + 31) >> 5) & | ||
743 | CCDC_HSIZE_VAL_MASK; | ||
744 | |||
745 | dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to HSIZE...\n", | ||
746 | (((params->win.width * 2) + 31) >> 5) & | ||
747 | CCDC_HSIZE_VAL_MASK); | ||
748 | } | ||
749 | regw(val, HSIZE); | ||
750 | |||
751 | /* Configure SDOFST register */ | ||
752 | if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { | ||
753 | if (params->image_invert_enable) { | ||
754 | /* For interlace inverse mode */ | ||
755 | regw(CCDC_SDOFST_INTERLACE_INVERSE, SDOFST); | ||
756 | dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n", | ||
757 | CCDC_SDOFST_INTERLACE_INVERSE); | ||
758 | } else { | ||
759 | /* For interlace non inverse mode */ | ||
760 | regw(CCDC_SDOFST_INTERLACE_NORMAL, SDOFST); | ||
761 | dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n", | ||
762 | CCDC_SDOFST_INTERLACE_NORMAL); | ||
763 | } | ||
764 | } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { | ||
765 | if (params->image_invert_enable) { | ||
766 | /* For progessive inverse mode */ | ||
767 | regw(CCDC_SDOFST_PROGRESSIVE_INVERSE, SDOFST); | ||
768 | dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n", | ||
769 | CCDC_SDOFST_PROGRESSIVE_INVERSE); | ||
770 | } else { | ||
771 | /* For progessive non inverse mode */ | ||
772 | regw(CCDC_SDOFST_PROGRESSIVE_NORMAL, SDOFST); | ||
773 | dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n", | ||
774 | CCDC_SDOFST_PROGRESSIVE_NORMAL); | ||
775 | } | ||
776 | } | ||
777 | dev_dbg(ccdc_cfg.dev, "\nend of ccdc_config_raw..."); | ||
778 | return 0; | ||
779 | } | ||
780 | |||
781 | static int ccdc_configure(void) | ||
782 | { | ||
783 | if (ccdc_cfg.if_type == VPFE_RAW_BAYER) | ||
784 | return ccdc_config_raw(); | ||
785 | else | ||
786 | ccdc_config_ycbcr(); | ||
787 | return 0; | ||
788 | } | ||
789 | |||
790 | static int ccdc_set_buftype(enum ccdc_buftype buf_type) | ||
791 | { | ||
792 | if (ccdc_cfg.if_type == VPFE_RAW_BAYER) | ||
793 | ccdc_cfg.bayer.buf_type = buf_type; | ||
794 | else | ||
795 | ccdc_cfg.ycbcr.buf_type = buf_type; | ||
796 | return 0; | ||
797 | } | ||
798 | static enum ccdc_buftype ccdc_get_buftype(void) | ||
799 | { | ||
800 | if (ccdc_cfg.if_type == VPFE_RAW_BAYER) | ||
801 | return ccdc_cfg.bayer.buf_type; | ||
802 | return ccdc_cfg.ycbcr.buf_type; | ||
803 | } | ||
804 | |||
805 | static int ccdc_enum_pix(u32 *pix, int i) | ||
806 | { | ||
807 | int ret = -EINVAL; | ||
808 | if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { | ||
809 | if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) { | ||
810 | *pix = ccdc_raw_bayer_pix_formats[i]; | ||
811 | ret = 0; | ||
812 | } | ||
813 | } else { | ||
814 | if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) { | ||
815 | *pix = ccdc_raw_yuv_pix_formats[i]; | ||
816 | ret = 0; | ||
817 | } | ||
818 | } | ||
819 | return ret; | ||
820 | } | ||
821 | |||
822 | static int ccdc_set_pixel_format(u32 pixfmt) | ||
823 | { | ||
824 | struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw; | ||
825 | |||
826 | if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { | ||
827 | ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; | ||
828 | if (pixfmt == V4L2_PIX_FMT_SBGGR8) | ||
829 | alaw->enable = 1; | ||
830 | else if (pixfmt != V4L2_PIX_FMT_SBGGR16) | ||
831 | return -EINVAL; | ||
832 | } else { | ||
833 | if (pixfmt == V4L2_PIX_FMT_YUYV) | ||
834 | ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; | ||
835 | else if (pixfmt == V4L2_PIX_FMT_UYVY) | ||
836 | ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; | ||
837 | else | ||
838 | return -EINVAL; | ||
839 | } | ||
840 | return 0; | ||
841 | } | ||
842 | static u32 ccdc_get_pixel_format(void) | ||
843 | { | ||
844 | struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw; | ||
845 | u32 pixfmt; | ||
846 | |||
847 | if (ccdc_cfg.if_type == VPFE_RAW_BAYER) | ||
848 | if (alaw->enable) | ||
849 | pixfmt = V4L2_PIX_FMT_SBGGR8; | ||
850 | else | ||
851 | pixfmt = V4L2_PIX_FMT_SBGGR16; | ||
852 | else { | ||
853 | if (ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) | ||
854 | pixfmt = V4L2_PIX_FMT_YUYV; | ||
855 | else | ||
856 | pixfmt = V4L2_PIX_FMT_UYVY; | ||
857 | } | ||
858 | return pixfmt; | ||
859 | } | ||
860 | static int ccdc_set_image_window(struct v4l2_rect *win) | ||
861 | { | ||
862 | if (ccdc_cfg.if_type == VPFE_RAW_BAYER) | ||
863 | ccdc_cfg.bayer.win = *win; | ||
864 | else | ||
865 | ccdc_cfg.ycbcr.win = *win; | ||
866 | return 0; | ||
867 | } | ||
868 | |||
869 | static void ccdc_get_image_window(struct v4l2_rect *win) | ||
870 | { | ||
871 | if (ccdc_cfg.if_type == VPFE_RAW_BAYER) | ||
872 | *win = ccdc_cfg.bayer.win; | ||
873 | else | ||
874 | *win = ccdc_cfg.ycbcr.win; | ||
875 | } | ||
876 | |||
877 | static unsigned int ccdc_get_line_length(void) | ||
878 | { | ||
879 | struct ccdc_config_params_raw *config_params = | ||
880 | &ccdc_cfg.bayer.config_params; | ||
881 | unsigned int len; | ||
882 | |||
883 | if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { | ||
884 | if ((config_params->alaw.enable) || | ||
885 | (config_params->data_sz == CCDC_DATA_8BITS)) | ||
886 | len = ccdc_cfg.bayer.win.width; | ||
887 | else | ||
888 | len = ccdc_cfg.bayer.win.width * 2; | ||
889 | } else | ||
890 | len = ccdc_cfg.ycbcr.win.width * 2; | ||
891 | return ALIGN(len, 32); | ||
892 | } | ||
893 | |||
894 | static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt) | ||
895 | { | ||
896 | if (ccdc_cfg.if_type == VPFE_RAW_BAYER) | ||
897 | ccdc_cfg.bayer.frm_fmt = frm_fmt; | ||
898 | else | ||
899 | ccdc_cfg.ycbcr.frm_fmt = frm_fmt; | ||
900 | return 0; | ||
901 | } | ||
902 | |||
903 | static enum ccdc_frmfmt ccdc_get_frame_format(void) | ||
904 | { | ||
905 | if (ccdc_cfg.if_type == VPFE_RAW_BAYER) | ||
906 | return ccdc_cfg.bayer.frm_fmt; | ||
907 | else | ||
908 | return ccdc_cfg.ycbcr.frm_fmt; | ||
909 | } | ||
910 | |||
911 | static int ccdc_getfid(void) | ||
912 | { | ||
913 | return (regr(MODESET) >> 15) & 1; | ||
914 | } | ||
915 | |||
916 | /* misc operations */ | ||
917 | static inline void ccdc_setfbaddr(unsigned long addr) | ||
918 | { | ||
919 | regw((addr >> 21) & 0x007f, STADRH); | ||
920 | regw((addr >> 5) & 0x0ffff, STADRL); | ||
921 | } | ||
922 | |||
923 | static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params) | ||
924 | { | ||
925 | ccdc_cfg.if_type = params->if_type; | ||
926 | |||
927 | switch (params->if_type) { | ||
928 | case VPFE_BT656: | ||
929 | case VPFE_YCBCR_SYNC_16: | ||
930 | case VPFE_YCBCR_SYNC_8: | ||
931 | ccdc_cfg.ycbcr.vd_pol = params->vdpol; | ||
932 | ccdc_cfg.ycbcr.hd_pol = params->hdpol; | ||
933 | break; | ||
934 | default: | ||
935 | /* TODO add support for raw bayer here */ | ||
936 | return -EINVAL; | ||
937 | } | ||
938 | return 0; | ||
939 | } | ||
940 | |||
941 | static struct ccdc_hw_device ccdc_hw_dev = { | ||
942 | .name = "DM355 CCDC", | ||
943 | .owner = THIS_MODULE, | ||
944 | .hw_ops = { | ||
945 | .open = ccdc_open, | ||
946 | .close = ccdc_close, | ||
947 | .enable = ccdc_enable, | ||
948 | .enable_out_to_sdram = ccdc_enable_output_to_sdram, | ||
949 | .set_hw_if_params = ccdc_set_hw_if_params, | ||
950 | .set_params = ccdc_set_params, | ||
951 | .configure = ccdc_configure, | ||
952 | .set_buftype = ccdc_set_buftype, | ||
953 | .get_buftype = ccdc_get_buftype, | ||
954 | .enum_pix = ccdc_enum_pix, | ||
955 | .set_pixel_format = ccdc_set_pixel_format, | ||
956 | .get_pixel_format = ccdc_get_pixel_format, | ||
957 | .set_frame_format = ccdc_set_frame_format, | ||
958 | .get_frame_format = ccdc_get_frame_format, | ||
959 | .set_image_window = ccdc_set_image_window, | ||
960 | .get_image_window = ccdc_get_image_window, | ||
961 | .get_line_length = ccdc_get_line_length, | ||
962 | .setfbaddr = ccdc_setfbaddr, | ||
963 | .getfid = ccdc_getfid, | ||
964 | }, | ||
965 | }; | ||
966 | |||
967 | static int __init dm355_ccdc_probe(struct platform_device *pdev) | ||
968 | { | ||
969 | void (*setup_pinmux)(void); | ||
970 | struct resource *res; | ||
971 | int status = 0; | ||
972 | |||
973 | /* | ||
974 | * first try to register with vpfe. If not correct platform, then we | ||
975 | * don't have to iomap | ||
976 | */ | ||
977 | status = vpfe_register_ccdc_device(&ccdc_hw_dev); | ||
978 | if (status < 0) | ||
979 | return status; | ||
980 | |||
981 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
982 | if (!res) { | ||
983 | status = -ENODEV; | ||
984 | goto fail_nores; | ||
985 | } | ||
986 | |||
987 | res = request_mem_region(res->start, resource_size(res), res->name); | ||
988 | if (!res) { | ||
989 | status = -EBUSY; | ||
990 | goto fail_nores; | ||
991 | } | ||
992 | |||
993 | ccdc_cfg.base_addr = ioremap_nocache(res->start, resource_size(res)); | ||
994 | if (!ccdc_cfg.base_addr) { | ||
995 | status = -ENOMEM; | ||
996 | goto fail_nomem; | ||
997 | } | ||
998 | |||
999 | /* Get and enable Master clock */ | ||
1000 | ccdc_cfg.mclk = clk_get(&pdev->dev, "master"); | ||
1001 | if (IS_ERR(ccdc_cfg.mclk)) { | ||
1002 | status = PTR_ERR(ccdc_cfg.mclk); | ||
1003 | goto fail_nomap; | ||
1004 | } | ||
1005 | if (clk_enable(ccdc_cfg.mclk)) { | ||
1006 | status = -ENODEV; | ||
1007 | goto fail_mclk; | ||
1008 | } | ||
1009 | |||
1010 | /* Get and enable Slave clock */ | ||
1011 | ccdc_cfg.sclk = clk_get(&pdev->dev, "slave"); | ||
1012 | if (IS_ERR(ccdc_cfg.sclk)) { | ||
1013 | status = PTR_ERR(ccdc_cfg.sclk); | ||
1014 | goto fail_mclk; | ||
1015 | } | ||
1016 | if (clk_enable(ccdc_cfg.sclk)) { | ||
1017 | status = -ENODEV; | ||
1018 | goto fail_sclk; | ||
1019 | } | ||
1020 | |||
1021 | /* Platform data holds setup_pinmux function ptr */ | ||
1022 | if (NULL == pdev->dev.platform_data) { | ||
1023 | status = -ENODEV; | ||
1024 | goto fail_sclk; | ||
1025 | } | ||
1026 | setup_pinmux = pdev->dev.platform_data; | ||
1027 | /* | ||
1028 | * setup Mux configuration for ccdc which may be different for | ||
1029 | * different SoCs using this CCDC | ||
1030 | */ | ||
1031 | setup_pinmux(); | ||
1032 | ccdc_cfg.dev = &pdev->dev; | ||
1033 | printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name); | ||
1034 | return 0; | ||
1035 | fail_sclk: | ||
1036 | clk_put(ccdc_cfg.sclk); | ||
1037 | fail_mclk: | ||
1038 | clk_put(ccdc_cfg.mclk); | ||
1039 | fail_nomap: | ||
1040 | iounmap(ccdc_cfg.base_addr); | ||
1041 | fail_nomem: | ||
1042 | release_mem_region(res->start, resource_size(res)); | ||
1043 | fail_nores: | ||
1044 | vpfe_unregister_ccdc_device(&ccdc_hw_dev); | ||
1045 | return status; | ||
1046 | } | ||
1047 | |||
1048 | static int dm355_ccdc_remove(struct platform_device *pdev) | ||
1049 | { | ||
1050 | struct resource *res; | ||
1051 | |||
1052 | clk_put(ccdc_cfg.mclk); | ||
1053 | clk_put(ccdc_cfg.sclk); | ||
1054 | iounmap(ccdc_cfg.base_addr); | ||
1055 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
1056 | if (res) | ||
1057 | release_mem_region(res->start, resource_size(res)); | ||
1058 | vpfe_unregister_ccdc_device(&ccdc_hw_dev); | ||
1059 | return 0; | ||
1060 | } | ||
1061 | |||
1062 | static struct platform_driver dm355_ccdc_driver = { | ||
1063 | .driver = { | ||
1064 | .name = "dm355_ccdc", | ||
1065 | .owner = THIS_MODULE, | ||
1066 | }, | ||
1067 | .remove = __devexit_p(dm355_ccdc_remove), | ||
1068 | .probe = dm355_ccdc_probe, | ||
1069 | }; | ||
1070 | |||
1071 | static int __init dm355_ccdc_init(void) | ||
1072 | { | ||
1073 | return platform_driver_register(&dm355_ccdc_driver); | ||
1074 | } | ||
1075 | |||
1076 | static void __exit dm355_ccdc_exit(void) | ||
1077 | { | ||
1078 | platform_driver_unregister(&dm355_ccdc_driver); | ||
1079 | } | ||
1080 | |||
1081 | module_init(dm355_ccdc_init); | ||
1082 | module_exit(dm355_ccdc_exit); | ||