Merge git://git.infradead.org/mtd-2.6
[pandora-kernel.git] / drivers / media / video / s5p-fimc / fimc-reg.c
1 /*
2  * Register interface file for Samsung Camera Interface (FIMC) driver
3  *
4  * Copyright (c) 2010 Samsung Electronics
5  *
6  * Sylwester Nawrocki, s.nawrocki@samsung.com
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11 */
12
13 #include <linux/io.h>
14 #include <linux/delay.h>
15 #include <mach/map.h>
16
17 #include "fimc-core.h"
18
19
20 void fimc_hw_reset(struct fimc_dev *dev)
21 {
22         u32 cfg;
23
24         cfg = readl(dev->regs + S5P_CISRCFMT);
25         cfg |= S5P_CISRCFMT_ITU601_8BIT;
26         writel(cfg, dev->regs + S5P_CISRCFMT);
27
28         /* Software reset. */
29         cfg = readl(dev->regs + S5P_CIGCTRL);
30         cfg |= (S5P_CIGCTRL_SWRST | S5P_CIGCTRL_IRQ_LEVEL);
31         writel(cfg, dev->regs + S5P_CIGCTRL);
32         msleep(1);
33
34         cfg = readl(dev->regs + S5P_CIGCTRL);
35         cfg &= ~S5P_CIGCTRL_SWRST;
36         writel(cfg, dev->regs + S5P_CIGCTRL);
37
38 }
39
40 void fimc_hw_set_rotation(struct fimc_ctx *ctx)
41 {
42         u32 cfg, flip;
43         struct fimc_dev *dev = ctx->fimc_dev;
44
45         cfg = readl(dev->regs + S5P_CITRGFMT);
46         cfg &= ~(S5P_CITRGFMT_INROT90 | S5P_CITRGFMT_OUTROT90);
47
48         flip = readl(dev->regs + S5P_MSCTRL);
49         flip &= ~S5P_MSCTRL_FLIP_MASK;
50
51         /*
52          * The input and output rotator cannot work simultaneously.
53          * Use the output rotator in output DMA mode or the input rotator
54          * in direct fifo output mode.
55          */
56         if (ctx->rotation == 90 || ctx->rotation == 270) {
57                 if (ctx->out_path == FIMC_LCDFIFO) {
58                         cfg |= S5P_CITRGFMT_INROT90;
59                         if (ctx->rotation == 270)
60                                 flip |= S5P_MSCTRL_FLIP_180;
61                 } else {
62                         cfg |= S5P_CITRGFMT_OUTROT90;
63                         if (ctx->rotation == 270)
64                                 cfg |= S5P_CITRGFMT_FLIP_180;
65                 }
66         } else if (ctx->rotation == 180) {
67                 if (ctx->out_path == FIMC_LCDFIFO)
68                         flip |= S5P_MSCTRL_FLIP_180;
69                 else
70                         cfg |= S5P_CITRGFMT_FLIP_180;
71         }
72         if (ctx->rotation == 180 || ctx->rotation == 270)
73                 writel(flip, dev->regs + S5P_MSCTRL);
74         writel(cfg, dev->regs + S5P_CITRGFMT);
75 }
76
77 static u32 fimc_hw_get_in_flip(u32 ctx_flip)
78 {
79         u32 flip = S5P_MSCTRL_FLIP_NORMAL;
80
81         switch (ctx_flip) {
82         case FLIP_X_AXIS:
83                 flip = S5P_MSCTRL_FLIP_X_MIRROR;
84                 break;
85         case FLIP_Y_AXIS:
86                 flip = S5P_MSCTRL_FLIP_Y_MIRROR;
87                 break;
88         case FLIP_XY_AXIS:
89                 flip = S5P_MSCTRL_FLIP_180;
90                 break;
91         }
92
93         return flip;
94 }
95
96 static u32 fimc_hw_get_target_flip(u32 ctx_flip)
97 {
98         u32 flip = S5P_CITRGFMT_FLIP_NORMAL;
99
100         switch (ctx_flip) {
101         case FLIP_X_AXIS:
102                 flip = S5P_CITRGFMT_FLIP_X_MIRROR;
103                 break;
104         case FLIP_Y_AXIS:
105                 flip = S5P_CITRGFMT_FLIP_Y_MIRROR;
106                 break;
107         case FLIP_XY_AXIS:
108                 flip = S5P_CITRGFMT_FLIP_180;
109                 break;
110         case FLIP_NONE:
111                 break;
112
113         }
114         return flip;
115 }
116
117 void fimc_hw_set_target_format(struct fimc_ctx *ctx)
118 {
119         u32 cfg;
120         struct fimc_dev *dev = ctx->fimc_dev;
121         struct fimc_frame *frame = &ctx->d_frame;
122
123         dbg("w= %d, h= %d color: %d", frame->width,
124                 frame->height, frame->fmt->color);
125
126         cfg = readl(dev->regs + S5P_CITRGFMT);
127         cfg &= ~(S5P_CITRGFMT_FMT_MASK | S5P_CITRGFMT_HSIZE_MASK |
128                   S5P_CITRGFMT_VSIZE_MASK);
129
130         switch (frame->fmt->color) {
131         case S5P_FIMC_RGB565:
132         case S5P_FIMC_RGB666:
133         case S5P_FIMC_RGB888:
134                 cfg |= S5P_CITRGFMT_RGB;
135                 break;
136         case S5P_FIMC_YCBCR420:
137                 cfg |= S5P_CITRGFMT_YCBCR420;
138                 break;
139         case S5P_FIMC_YCBYCR422:
140         case S5P_FIMC_YCRYCB422:
141         case S5P_FIMC_CBYCRY422:
142         case S5P_FIMC_CRYCBY422:
143                 if (frame->fmt->planes_cnt == 1)
144                         cfg |= S5P_CITRGFMT_YCBCR422_1P;
145                 else
146                         cfg |= S5P_CITRGFMT_YCBCR422;
147                 break;
148         default:
149                 break;
150         }
151
152         cfg |= S5P_CITRGFMT_HSIZE(frame->width);
153         cfg |= S5P_CITRGFMT_VSIZE(frame->height);
154
155         if (ctx->rotation == 0) {
156                 cfg &= ~S5P_CITRGFMT_FLIP_MASK;
157                 cfg |= fimc_hw_get_target_flip(ctx->flip);
158         }
159         writel(cfg, dev->regs + S5P_CITRGFMT);
160
161         cfg = readl(dev->regs + S5P_CITAREA) & ~S5P_CITAREA_MASK;
162         cfg |= (frame->width * frame->height);
163         writel(cfg, dev->regs + S5P_CITAREA);
164 }
165
166 static void fimc_hw_set_out_dma_size(struct fimc_ctx *ctx)
167 {
168         struct fimc_dev *dev = ctx->fimc_dev;
169         struct fimc_frame *frame = &ctx->d_frame;
170         u32 cfg = 0;
171
172         if (ctx->rotation == 90 || ctx->rotation == 270) {
173                 cfg |= S5P_ORIG_SIZE_HOR(frame->f_height);
174                 cfg |= S5P_ORIG_SIZE_VER(frame->f_width);
175         } else {
176                 cfg |= S5P_ORIG_SIZE_HOR(frame->f_width);
177                 cfg |= S5P_ORIG_SIZE_VER(frame->f_height);
178         }
179         writel(cfg, dev->regs + S5P_ORGOSIZE);
180 }
181
182 void fimc_hw_set_out_dma(struct fimc_ctx *ctx)
183 {
184         u32 cfg;
185         struct fimc_dev *dev = ctx->fimc_dev;
186         struct fimc_frame *frame = &ctx->d_frame;
187         struct fimc_dma_offset *offset = &frame->dma_offset;
188
189         /* Set the input dma offsets. */
190         cfg = 0;
191         cfg |= S5P_CIO_OFFS_HOR(offset->y_h);
192         cfg |= S5P_CIO_OFFS_VER(offset->y_v);
193         writel(cfg, dev->regs + S5P_CIOYOFF);
194
195         cfg = 0;
196         cfg |= S5P_CIO_OFFS_HOR(offset->cb_h);
197         cfg |= S5P_CIO_OFFS_VER(offset->cb_v);
198         writel(cfg, dev->regs + S5P_CIOCBOFF);
199
200         cfg = 0;
201         cfg |= S5P_CIO_OFFS_HOR(offset->cr_h);
202         cfg |= S5P_CIO_OFFS_VER(offset->cr_v);
203         writel(cfg, dev->regs + S5P_CIOCROFF);
204
205         fimc_hw_set_out_dma_size(ctx);
206
207         /* Configure chroma components order. */
208         cfg = readl(dev->regs + S5P_CIOCTRL);
209
210         cfg &= ~(S5P_CIOCTRL_ORDER2P_MASK | S5P_CIOCTRL_ORDER422_MASK |
211                  S5P_CIOCTRL_YCBCR_PLANE_MASK);
212
213         if (frame->fmt->planes_cnt == 1)
214                 cfg |= ctx->out_order_1p;
215         else if (frame->fmt->planes_cnt == 2)
216                 cfg |= ctx->out_order_2p | S5P_CIOCTRL_YCBCR_2PLANE;
217         else if (frame->fmt->planes_cnt == 3)
218                 cfg |= S5P_CIOCTRL_YCBCR_3PLANE;
219
220         writel(cfg, dev->regs + S5P_CIOCTRL);
221 }
222
223 static void fimc_hw_en_autoload(struct fimc_dev *dev, int enable)
224 {
225         u32 cfg = readl(dev->regs + S5P_ORGISIZE);
226         if (enable)
227                 cfg |= S5P_CIREAL_ISIZE_AUTOLOAD_EN;
228         else
229                 cfg &= ~S5P_CIREAL_ISIZE_AUTOLOAD_EN;
230         writel(cfg, dev->regs + S5P_ORGISIZE);
231 }
232
233 void fimc_hw_en_lastirq(struct fimc_dev *dev, int enable)
234 {
235         unsigned long flags;
236         u32 cfg;
237
238         spin_lock_irqsave(&dev->slock, flags);
239
240         cfg = readl(dev->regs + S5P_CIOCTRL);
241         if (enable)
242                 cfg |= S5P_CIOCTRL_LASTIRQ_ENABLE;
243         else
244                 cfg &= ~S5P_CIOCTRL_LASTIRQ_ENABLE;
245         writel(cfg, dev->regs + S5P_CIOCTRL);
246
247         spin_unlock_irqrestore(&dev->slock, flags);
248 }
249
250 void fimc_hw_set_prescaler(struct fimc_ctx *ctx)
251 {
252         struct fimc_dev *dev =  ctx->fimc_dev;
253         struct fimc_scaler *sc = &ctx->scaler;
254         u32 cfg = 0, shfactor;
255
256         shfactor = 10 - (sc->hfactor + sc->vfactor);
257
258         cfg |= S5P_CISCPRERATIO_SHFACTOR(shfactor);
259         cfg |= S5P_CISCPRERATIO_HOR(sc->pre_hratio);
260         cfg |= S5P_CISCPRERATIO_VER(sc->pre_vratio);
261         writel(cfg, dev->regs + S5P_CISCPRERATIO);
262
263         cfg = 0;
264         cfg |= S5P_CISCPREDST_WIDTH(sc->pre_dst_width);
265         cfg |= S5P_CISCPREDST_HEIGHT(sc->pre_dst_height);
266         writel(cfg, dev->regs + S5P_CISCPREDST);
267 }
268
269 void fimc_hw_set_scaler(struct fimc_ctx *ctx)
270 {
271         struct fimc_dev *dev = ctx->fimc_dev;
272         struct fimc_scaler *sc = &ctx->scaler;
273         struct fimc_frame *src_frame = &ctx->s_frame;
274         struct fimc_frame *dst_frame = &ctx->d_frame;
275         u32 cfg = 0;
276
277         if (!(ctx->flags & FIMC_COLOR_RANGE_NARROW))
278                 cfg |= (S5P_CISCCTRL_CSCR2Y_WIDE | S5P_CISCCTRL_CSCY2R_WIDE);
279
280         if (!sc->enabled)
281                 cfg |= S5P_CISCCTRL_SCALERBYPASS;
282
283         if (sc->scaleup_h)
284                 cfg |= S5P_CISCCTRL_SCALEUP_H;
285
286         if (sc->scaleup_v)
287                 cfg |= S5P_CISCCTRL_SCALEUP_V;
288
289         if (sc->copy_mode)
290                 cfg |= S5P_CISCCTRL_ONE2ONE;
291
292
293         if (ctx->in_path == FIMC_DMA) {
294                 if (src_frame->fmt->color == S5P_FIMC_RGB565)
295                         cfg |= S5P_CISCCTRL_INRGB_FMT_RGB565;
296                 else if (src_frame->fmt->color == S5P_FIMC_RGB666)
297                         cfg |= S5P_CISCCTRL_INRGB_FMT_RGB666;
298                 else if (src_frame->fmt->color == S5P_FIMC_RGB888)
299                         cfg |= S5P_CISCCTRL_INRGB_FMT_RGB888;
300         }
301
302         if (ctx->out_path == FIMC_DMA) {
303                 if (dst_frame->fmt->color == S5P_FIMC_RGB565)
304                         cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB565;
305                 else if (dst_frame->fmt->color == S5P_FIMC_RGB666)
306                         cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB666;
307                 else if (dst_frame->fmt->color == S5P_FIMC_RGB888)
308                         cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888;
309         } else {
310                 cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888;
311
312                 if (ctx->flags & FIMC_SCAN_MODE_INTERLACED)
313                         cfg |= S5P_CISCCTRL_INTERLACE;
314         }
315
316         dbg("main_hratio= 0x%X  main_vratio= 0x%X",
317                 sc->main_hratio, sc->main_vratio);
318
319         cfg |= S5P_CISCCTRL_SC_HORRATIO(sc->main_hratio);
320         cfg |= S5P_CISCCTRL_SC_VERRATIO(sc->main_vratio);
321
322         writel(cfg, dev->regs + S5P_CISCCTRL);
323 }
324
325 void fimc_hw_en_capture(struct fimc_ctx *ctx)
326 {
327         struct fimc_dev *dev = ctx->fimc_dev;
328         u32 cfg;
329
330         cfg = readl(dev->regs + S5P_CIIMGCPT);
331         /* One shot mode for output DMA or freerun for FIFO. */
332         if (ctx->out_path == FIMC_DMA)
333                 cfg |= S5P_CIIMGCPT_CPT_FREN_ENABLE;
334         else
335                 cfg &= ~S5P_CIIMGCPT_CPT_FREN_ENABLE;
336
337         if (ctx->scaler.enabled)
338                 cfg |= S5P_CIIMGCPT_IMGCPTEN_SC;
339
340         writel(cfg | S5P_CIIMGCPT_IMGCPTEN, dev->regs + S5P_CIIMGCPT);
341 }
342
343 void fimc_hw_set_effect(struct fimc_ctx *ctx)
344 {
345         struct fimc_dev *dev = ctx->fimc_dev;
346         struct fimc_effect *effect = &ctx->effect;
347         u32 cfg = (S5P_CIIMGEFF_IE_ENABLE | S5P_CIIMGEFF_IE_SC_AFTER);
348
349         cfg |= effect->type;
350
351         if (effect->type == S5P_FIMC_EFFECT_ARBITRARY) {
352                 cfg |= S5P_CIIMGEFF_PAT_CB(effect->pat_cb);
353                 cfg |= S5P_CIIMGEFF_PAT_CR(effect->pat_cr);
354         }
355
356         writel(cfg, dev->regs + S5P_CIIMGEFF);
357 }
358
359 static void fimc_hw_set_in_dma_size(struct fimc_ctx *ctx)
360 {
361         struct fimc_dev *dev = ctx->fimc_dev;
362         struct fimc_frame *frame = &ctx->s_frame;
363         u32 cfg_o = 0;
364         u32 cfg_r = 0;
365
366         if (FIMC_LCDFIFO == ctx->out_path)
367                 cfg_r |=  S5P_CIREAL_ISIZE_AUTOLOAD_EN;
368
369         cfg_o |= S5P_ORIG_SIZE_HOR(frame->f_width);
370         cfg_o |= S5P_ORIG_SIZE_VER(frame->f_height);
371         cfg_r |= S5P_CIREAL_ISIZE_WIDTH(frame->width);
372         cfg_r |= S5P_CIREAL_ISIZE_HEIGHT(frame->height);
373
374         writel(cfg_o, dev->regs + S5P_ORGISIZE);
375         writel(cfg_r, dev->regs + S5P_CIREAL_ISIZE);
376 }
377
378 void fimc_hw_set_in_dma(struct fimc_ctx *ctx)
379 {
380         struct fimc_dev *dev = ctx->fimc_dev;
381         struct fimc_frame *frame = &ctx->s_frame;
382         struct fimc_dma_offset *offset = &frame->dma_offset;
383         u32 cfg = 0;
384
385         /* Set the pixel offsets. */
386         cfg |= S5P_CIO_OFFS_HOR(offset->y_h);
387         cfg |= S5P_CIO_OFFS_VER(offset->y_v);
388         writel(cfg, dev->regs + S5P_CIIYOFF);
389
390         cfg = 0;
391         cfg |= S5P_CIO_OFFS_HOR(offset->cb_h);
392         cfg |= S5P_CIO_OFFS_VER(offset->cb_v);
393         writel(cfg, dev->regs + S5P_CIICBOFF);
394
395         cfg = 0;
396         cfg |= S5P_CIO_OFFS_HOR(offset->cr_h);
397         cfg |= S5P_CIO_OFFS_VER(offset->cr_v);
398         writel(cfg, dev->regs + S5P_CIICROFF);
399
400         /* Input original and real size. */
401         fimc_hw_set_in_dma_size(ctx);
402
403         /* Autoload is used currently only in FIFO mode. */
404         fimc_hw_en_autoload(dev, ctx->out_path == FIMC_LCDFIFO);
405
406         /* Set the input DMA to process single frame only. */
407         cfg = readl(dev->regs + S5P_MSCTRL);
408         cfg &= ~(S5P_MSCTRL_FLIP_MASK
409                 | S5P_MSCTRL_INFORMAT_MASK
410                 | S5P_MSCTRL_IN_BURST_COUNT_MASK
411                 | S5P_MSCTRL_INPUT_MASK
412                 | S5P_MSCTRL_C_INT_IN_MASK
413                 | S5P_MSCTRL_2P_IN_ORDER_MASK);
414
415         cfg |= (S5P_MSCTRL_FRAME_COUNT(1) | S5P_MSCTRL_INPUT_MEMORY);
416
417         switch (frame->fmt->color) {
418         case S5P_FIMC_RGB565:
419         case S5P_FIMC_RGB666:
420         case S5P_FIMC_RGB888:
421                 cfg |= S5P_MSCTRL_INFORMAT_RGB;
422                 break;
423         case S5P_FIMC_YCBCR420:
424                 cfg |= S5P_MSCTRL_INFORMAT_YCBCR420;
425
426                 if (frame->fmt->planes_cnt == 2)
427                         cfg |= ctx->in_order_2p | S5P_MSCTRL_C_INT_IN_2PLANE;
428                 else
429                         cfg |= S5P_MSCTRL_C_INT_IN_3PLANE;
430
431                 break;
432         case S5P_FIMC_YCBYCR422:
433         case S5P_FIMC_YCRYCB422:
434         case S5P_FIMC_CBYCRY422:
435         case S5P_FIMC_CRYCBY422:
436                 if (frame->fmt->planes_cnt == 1) {
437                         cfg |= ctx->in_order_1p
438                                 | S5P_MSCTRL_INFORMAT_YCBCR422_1P;
439                 } else {
440                         cfg |= S5P_MSCTRL_INFORMAT_YCBCR422;
441
442                         if (frame->fmt->planes_cnt == 2)
443                                 cfg |= ctx->in_order_2p
444                                         | S5P_MSCTRL_C_INT_IN_2PLANE;
445                         else
446                                 cfg |= S5P_MSCTRL_C_INT_IN_3PLANE;
447                 }
448                 break;
449         default:
450                 break;
451         }
452
453         /*
454          * Input DMA flip mode (and rotation).
455          * Do not allow simultaneous rotation and flipping.
456          */
457         if (!ctx->rotation && ctx->out_path == FIMC_LCDFIFO)
458                 cfg |= fimc_hw_get_in_flip(ctx->flip);
459
460         writel(cfg, dev->regs + S5P_MSCTRL);
461
462         /* Input/output DMA linear/tiled mode. */
463         cfg = readl(dev->regs + S5P_CIDMAPARAM);
464         cfg &= ~S5P_CIDMAPARAM_TILE_MASK;
465
466         if (tiled_fmt(ctx->s_frame.fmt))
467                 cfg |= S5P_CIDMAPARAM_R_64X32;
468
469         if (tiled_fmt(ctx->d_frame.fmt))
470                 cfg |= S5P_CIDMAPARAM_W_64X32;
471
472         writel(cfg, dev->regs + S5P_CIDMAPARAM);
473 }
474
475
476 void fimc_hw_set_input_path(struct fimc_ctx *ctx)
477 {
478         struct fimc_dev *dev = ctx->fimc_dev;
479
480         u32 cfg = readl(dev->regs + S5P_MSCTRL);
481         cfg &= ~S5P_MSCTRL_INPUT_MASK;
482
483         if (ctx->in_path == FIMC_DMA)
484                 cfg |= S5P_MSCTRL_INPUT_MEMORY;
485         else
486                 cfg |= S5P_MSCTRL_INPUT_EXTCAM;
487
488         writel(cfg, dev->regs + S5P_MSCTRL);
489 }
490
491 void fimc_hw_set_output_path(struct fimc_ctx *ctx)
492 {
493         struct fimc_dev *dev = ctx->fimc_dev;
494
495         u32 cfg = readl(dev->regs + S5P_CISCCTRL);
496         cfg &= ~S5P_CISCCTRL_LCDPATHEN_FIFO;
497         if (ctx->out_path == FIMC_LCDFIFO)
498                 cfg |= S5P_CISCCTRL_LCDPATHEN_FIFO;
499         writel(cfg, dev->regs + S5P_CISCCTRL);
500 }
501
502 void fimc_hw_set_input_addr(struct fimc_dev *dev, struct fimc_addr *paddr)
503 {
504         u32 cfg = 0;
505
506         cfg = readl(dev->regs + S5P_CIREAL_ISIZE);
507         cfg |= S5P_CIREAL_ISIZE_ADDR_CH_DIS;
508         writel(cfg, dev->regs + S5P_CIREAL_ISIZE);
509
510         writel(paddr->y, dev->regs + S5P_CIIYSA0);
511         writel(paddr->cb, dev->regs + S5P_CIICBSA0);
512         writel(paddr->cr, dev->regs + S5P_CIICRSA0);
513
514         cfg &= ~S5P_CIREAL_ISIZE_ADDR_CH_DIS;
515         writel(cfg, dev->regs + S5P_CIREAL_ISIZE);
516 }
517
518 void fimc_hw_set_output_addr(struct fimc_dev *dev, struct fimc_addr *paddr)
519 {
520         int i;
521         /* Set all the output register sets to point to single video buffer. */
522         for (i = 0; i < FIMC_MAX_OUT_BUFS; i++) {
523                 writel(paddr->y, dev->regs + S5P_CIOYSA(i));
524                 writel(paddr->cb, dev->regs + S5P_CIOCBSA(i));
525                 writel(paddr->cr, dev->regs + S5P_CIOCRSA(i));
526         }
527 }