2
* Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
6
* The code contained herein is licensed under the GNU General Public
7
* License. You may obtain a copy of the GNU General Public License
8
* Version 2 or later at the following locations:
10
* http://www.opensource.org/licenses/gpl-license.html
11
* http://www.gnu.org/copyleft/gpl.html
17
* @brief IPU IC functions
21
#include <linux/types.h>
22
#include <linux/init.h>
23
#include <linux/errno.h>
24
#include <linux/spinlock.h>
26
#include <linux/ipu.h>
30
#include "ipu_param_mem.h"
35
IC_TASK_POST_PROCESSOR
38
extern int g_ipu_hw_rev;
39
static void _init_csc(uint8_t ic_task, ipu_color_space_t in_format,
40
ipu_color_space_t out_format);
41
static bool _calc_resize_coeffs(uint32_t inSize, uint32_t outSize,
42
uint32_t * resizeCoeff,
43
uint32_t * downsizeCoeff);
45
void _ipu_ic_enable_task(ipu_channel_t channel)
49
ic_conf = __raw_readl(IC_CONF);
55
ic_conf |= IC_CONF_PRPVF_EN;
58
ic_conf |= IC_CONF_PRPVF_ROT_EN;
62
ic_conf |= IC_CONF_PRPENC_EN;
65
ic_conf |= IC_CONF_PRPENC_ROT_EN;
69
ic_conf |= IC_CONF_PP_EN;
72
ic_conf |= IC_CONF_PP_ROT_EN;
76
ic_conf |= IC_CONF_RWS_EN | IC_CONF_PRPENC_EN;
81
__raw_writel(ic_conf, IC_CONF);
84
void _ipu_ic_disable_task(ipu_channel_t channel)
88
ic_conf = __raw_readl(IC_CONF);
94
ic_conf &= ~IC_CONF_PRPVF_EN;
97
ic_conf &= ~IC_CONF_PRPVF_ROT_EN;
100
case MEM_PRP_ENC_MEM:
101
ic_conf &= ~IC_CONF_PRPENC_EN;
103
case MEM_ROT_ENC_MEM:
104
ic_conf &= ~IC_CONF_PRPENC_ROT_EN;
108
ic_conf &= ~IC_CONF_PP_EN;
111
ic_conf &= ~IC_CONF_PP_ROT_EN;
115
ic_conf &= ~(IC_CONF_RWS_EN | IC_CONF_PRPENC_EN);
120
__raw_writel(ic_conf, IC_CONF);
123
void _ipu_ic_init_prpvf(ipu_channel_params_t * params, bool src_is_csi)
125
uint32_t reg, ic_conf;
126
uint32_t downsizeCoeff, resizeCoeff;
127
ipu_color_space_t in_fmt, out_fmt;
129
/* Setup vertical resizing */
130
_calc_resize_coeffs(params->mem_prp_vf_mem.in_height,
131
params->mem_prp_vf_mem.out_height,
132
&resizeCoeff, &downsizeCoeff);
133
reg = (downsizeCoeff << 30) | (resizeCoeff << 16);
135
/* Setup horizontal resizing */
136
_calc_resize_coeffs(params->mem_prp_vf_mem.in_width,
137
params->mem_prp_vf_mem.out_width,
138
&resizeCoeff, &downsizeCoeff);
139
reg |= (downsizeCoeff << 14) | resizeCoeff;
141
__raw_writel(reg, IC_PRP_VF_RSC);
143
ic_conf = __raw_readl(IC_CONF);
145
/* Setup color space conversion */
146
in_fmt = format_to_colorspace(params->mem_prp_vf_mem.in_pixel_fmt);
147
out_fmt = format_to_colorspace(params->mem_prp_vf_mem.out_pixel_fmt);
149
if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
150
_init_csc(IC_TASK_VIEWFINDER, RGB, out_fmt);
151
ic_conf |= IC_CONF_PRPVF_CSC1; /* Enable RGB->YCBCR CSC */
154
if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
155
if (out_fmt == RGB) {
156
_init_csc(IC_TASK_VIEWFINDER, YCbCr, RGB);
157
ic_conf |= IC_CONF_PRPVF_CSC1; /* Enable YCBCR->RGB CSC */
159
/* TODO: Support YUV<->YCbCr conversion? */
163
if (params->mem_prp_vf_mem.graphics_combine_en) {
164
ic_conf |= IC_CONF_PRPVF_CMB;
166
/* need transparent CSC1 conversion */
167
_init_csc(IC_TASK_POST_PROCESSOR, RGB, RGB);
168
ic_conf |= IC_CONF_PRPVF_CSC1; /* Enable RGB->RGB CSC */
170
if (params->mem_prp_vf_mem.global_alpha_en) {
171
ic_conf |= IC_CONF_IC_GLB_LOC_A;
173
ic_conf &= ~IC_CONF_IC_GLB_LOC_A;
176
if (params->mem_prp_vf_mem.key_color_en) {
177
ic_conf |= IC_CONF_KEY_COLOR_EN;
179
ic_conf &= ~IC_CONF_KEY_COLOR_EN;
182
ic_conf &= ~IC_CONF_PP_CMB;
185
#ifndef CONFIG_VIRTIO_SUPPORT /* Setting RWS_EN doesn't work in Virtio */
187
ic_conf &= ~IC_CONF_RWS_EN;
189
ic_conf |= IC_CONF_RWS_EN;
192
__raw_writel(ic_conf, IC_CONF);
195
void _ipu_ic_uninit_prpvf(void)
199
reg = __raw_readl(IC_CONF);
200
reg &= ~(IC_CONF_PRPVF_EN | IC_CONF_PRPVF_CMB |
201
IC_CONF_PRPVF_CSC2 | IC_CONF_PRPVF_CSC1);
202
__raw_writel(reg, IC_CONF);
205
void _ipu_ic_init_rotate_vf(ipu_channel_params_t * params)
209
void _ipu_ic_uninit_rotate_vf(void)
212
reg = __raw_readl(IC_CONF);
213
reg &= ~IC_CONF_PRPVF_ROT_EN;
214
__raw_writel(reg, IC_CONF);
217
void _ipu_ic_init_csi(ipu_channel_params_t * params)
220
reg = __raw_readl(IC_CONF);
221
reg &= ~IC_CONF_CSI_MEM_WR_EN;
222
__raw_writel(reg, IC_CONF);
225
void _ipu_ic_uninit_csi(void)
228
reg = __raw_readl(IC_CONF);
229
reg &= ~(IC_CONF_RWS_EN | IC_CONF_PRPENC_EN);
230
__raw_writel(reg, IC_CONF);
233
void _ipu_ic_init_prpenc(ipu_channel_params_t * params, bool src_is_csi)
235
uint32_t reg, ic_conf;
236
uint32_t downsizeCoeff, resizeCoeff;
237
ipu_color_space_t in_fmt, out_fmt;
239
/* Setup vertical resizing */
240
_calc_resize_coeffs(params->mem_prp_enc_mem.in_height,
241
params->mem_prp_enc_mem.out_height,
242
&resizeCoeff, &downsizeCoeff);
243
reg = (downsizeCoeff << 30) | (resizeCoeff << 16);
245
/* Setup horizontal resizing */
246
_calc_resize_coeffs(params->mem_prp_enc_mem.in_width,
247
params->mem_prp_enc_mem.out_width,
248
&resizeCoeff, &downsizeCoeff);
249
reg |= (downsizeCoeff << 14) | resizeCoeff;
251
__raw_writel(reg, IC_PRP_ENC_RSC);
253
ic_conf = __raw_readl(IC_CONF);
255
/* Setup color space conversion */
256
in_fmt = format_to_colorspace(params->mem_prp_enc_mem.in_pixel_fmt);
257
out_fmt = format_to_colorspace(params->mem_prp_enc_mem.out_pixel_fmt);
259
if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
263
if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
264
if (out_fmt == RGB) {
265
_init_csc(IC_TASK_ENCODER, YCbCr, RGB);
266
ic_conf |= IC_CONF_PRPENC_CSC1; /* Enable YCBCR->RGB CSC */
268
/* TODO: Support YUV<->YCbCr conversion? */
273
ic_conf &= ~IC_CONF_RWS_EN;
275
ic_conf |= IC_CONF_RWS_EN;
278
__raw_writel(ic_conf, IC_CONF);
281
void _ipu_ic_uninit_prpenc(void)
285
reg = __raw_readl(IC_CONF);
286
reg &= ~(IC_CONF_PRPENC_EN | IC_CONF_PRPENC_CSC1);
287
__raw_writel(reg, IC_CONF);
290
void _ipu_ic_init_rotate_enc(ipu_channel_params_t * params)
294
void _ipu_ic_uninit_rotate_enc(void)
298
reg = __raw_readl(IC_CONF);
299
reg &= ~(IC_CONF_PRPENC_ROT_EN);
300
__raw_writel(reg, IC_CONF);
303
void _ipu_ic_init_pp(ipu_channel_params_t * params)
305
uint32_t reg, ic_conf;
306
uint32_t downsizeCoeff, resizeCoeff;
307
ipu_color_space_t in_fmt, out_fmt;
309
/* Setup vertical resizing */
310
_calc_resize_coeffs(params->mem_pp_mem.in_height,
311
params->mem_pp_mem.out_height,
312
&resizeCoeff, &downsizeCoeff);
313
reg = (downsizeCoeff << 30) | (resizeCoeff << 16);
315
/* Setup horizontal resizing */
316
_calc_resize_coeffs(params->mem_pp_mem.in_width,
317
params->mem_pp_mem.out_width,
318
&resizeCoeff, &downsizeCoeff);
319
reg |= (downsizeCoeff << 14) | resizeCoeff;
321
__raw_writel(reg, IC_PP_RSC);
323
ic_conf = __raw_readl(IC_CONF);
325
/* Setup color space conversion */
326
in_fmt = format_to_colorspace(params->mem_pp_mem.in_pixel_fmt);
327
out_fmt = format_to_colorspace(params->mem_pp_mem.out_pixel_fmt);
329
if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
330
_init_csc(IC_TASK_POST_PROCESSOR, RGB, out_fmt);
331
ic_conf |= IC_CONF_PP_CSC2; /* Enable RGB->YCBCR CSC */
334
if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
335
if (out_fmt == RGB) {
336
_init_csc(IC_TASK_POST_PROCESSOR, YCbCr, RGB);
337
ic_conf |= IC_CONF_PP_CSC1; /* Enable YCBCR->RGB CSC */
339
/* TODO: Support YUV<->YCbCr conversion? */
343
if (params->mem_pp_mem.graphics_combine_en) {
344
ic_conf |= IC_CONF_PP_CMB;
346
/* need transparent CSC1 conversion */
347
_init_csc(IC_TASK_POST_PROCESSOR, RGB, RGB);
348
ic_conf |= IC_CONF_PP_CSC1; /* Enable RGB->RGB CSC */
350
if (params->mem_pp_mem.global_alpha_en) {
351
ic_conf |= IC_CONF_IC_GLB_LOC_A;
353
ic_conf &= ~IC_CONF_IC_GLB_LOC_A;
356
if (params->mem_pp_mem.key_color_en) {
357
ic_conf |= IC_CONF_KEY_COLOR_EN;
359
ic_conf &= ~IC_CONF_KEY_COLOR_EN;
362
ic_conf &= ~IC_CONF_PP_CMB;
365
__raw_writel(ic_conf, IC_CONF);
368
void _ipu_ic_uninit_pp(void)
372
reg = __raw_readl(IC_CONF);
373
reg &= ~(IC_CONF_PP_EN | IC_CONF_PP_CSC1 | IC_CONF_PP_CSC2 |
375
__raw_writel(reg, IC_CONF);
378
void _ipu_ic_init_rotate_pp(ipu_channel_params_t * params)
382
void _ipu_ic_uninit_rotate_pp(void)
385
reg = __raw_readl(IC_CONF);
386
reg &= ~IC_CONF_PP_ROT_EN;
387
__raw_writel(reg, IC_CONF);
390
static void _init_csc(uint8_t ic_task, ipu_color_space_t in_format,
391
ipu_color_space_t out_format)
393
/* Y = R * .299 + G * .587 + B * .114;
394
U = R * -.169 + G * -.332 + B * .500 + 128.;
395
V = R * .500 + G * -.419 + B * -.0813 + 128.;*/
396
static const uint32_t rgb2ycbcr_coeff[4][3] = {
397
{0x004D, 0x0096, 0x001D},
398
{0x01D5, 0x01AB, 0x0080},
399
{0x0080, 0x0195, 0x01EB},
400
{0x0000, 0x0200, 0x0200}, /* A0, A1, A2 */
403
/* transparent RGB->RGB matrix for combining
405
static const uint32_t rgb2rgb_coeff[4][3] = {
406
{0x0080, 0x0000, 0x0000},
407
{0x0000, 0x0080, 0x0000},
408
{0x0000, 0x0000, 0x0080},
409
{0x0000, 0x0000, 0x0000}, /* A0, A1, A2 */
412
/* R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128));
413
G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128));
414
B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128); */
415
static const uint32_t ycbcr2rgb_coeff[4][3] = {
419
{8192 - 446, 266, 8192 - 554}, /* A0, A1, A2 */
423
uint32_t address = 0;
425
if (g_ipu_hw_rev > 1) {
426
if (ic_task == IC_TASK_VIEWFINDER) {
427
address = 0x645 << 3;
428
} else if (ic_task == IC_TASK_ENCODER) {
429
address = 0x321 << 3;
430
} else if (ic_task == IC_TASK_POST_PROCESSOR) {
431
address = 0x96C << 3;
436
if (ic_task == IC_TASK_VIEWFINDER) {
437
address = 0x5a5 << 3;
438
} else if (ic_task == IC_TASK_ENCODER) {
439
address = 0x2d1 << 3;
440
} else if (ic_task == IC_TASK_POST_PROCESSOR) {
441
address = 0x87c << 3;
447
if ((in_format == YCbCr) && (out_format == RGB)) {
448
/* Init CSC1 (YCbCr->RGB) */
450
(ycbcr2rgb_coeff[3][0] << 27) | (ycbcr2rgb_coeff[0][0] <<
452
(ycbcr2rgb_coeff[1][1] << 9) | ycbcr2rgb_coeff[2][2];
453
/* scale = 2, sat = 0 */
454
param[1] = (ycbcr2rgb_coeff[3][0] >> 5) | (2L << (40 - 32));
455
_ipu_write_param_mem(address, param, 2);
457
"addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
458
address, param[0], param[1]);
461
(ycbcr2rgb_coeff[3][1] << 27) | (ycbcr2rgb_coeff[0][1] <<
463
(ycbcr2rgb_coeff[1][0] << 9) | ycbcr2rgb_coeff[2][0];
464
param[1] = (ycbcr2rgb_coeff[3][1] >> 5);
466
_ipu_write_param_mem(address, param, 2);
468
"addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
469
address, param[0], param[1]);
472
(ycbcr2rgb_coeff[3][2] << 27) | (ycbcr2rgb_coeff[0][2] <<
474
(ycbcr2rgb_coeff[1][2] << 9) | ycbcr2rgb_coeff[2][1];
475
param[1] = (ycbcr2rgb_coeff[3][2] >> 5);
477
_ipu_write_param_mem(address, param, 2);
479
"addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
480
address, param[0], param[1]);
481
} else if ((in_format == RGB) && (out_format == YCbCr)) {
482
/* Init CSC1 (RGB->YCbCr) */
484
(rgb2ycbcr_coeff[3][0] << 27) | (rgb2ycbcr_coeff[0][0] <<
486
(rgb2ycbcr_coeff[1][1] << 9) | rgb2ycbcr_coeff[2][2];
487
/* scale = 1, sat = 0 */
488
param[1] = (rgb2ycbcr_coeff[3][0] >> 5) | (1UL << 8);
489
_ipu_write_param_mem(address, param, 2);
491
"addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
492
address, param[0], param[1]);
495
(rgb2ycbcr_coeff[3][1] << 27) | (rgb2ycbcr_coeff[0][1] <<
497
(rgb2ycbcr_coeff[1][0] << 9) | rgb2ycbcr_coeff[2][0];
498
param[1] = (rgb2ycbcr_coeff[3][1] >> 5);
500
_ipu_write_param_mem(address, param, 2);
502
"addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
503
address, param[0], param[1]);
506
(rgb2ycbcr_coeff[3][2] << 27) | (rgb2ycbcr_coeff[0][2] <<
508
(rgb2ycbcr_coeff[1][2] << 9) | rgb2ycbcr_coeff[2][1];
509
param[1] = (rgb2ycbcr_coeff[3][2] >> 5);
511
_ipu_write_param_mem(address, param, 2);
513
"addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
514
address, param[0], param[1]);
515
} else if ((in_format == RGB) && (out_format == RGB)) {
518
(rgb2rgb_coeff[3][0] << 27) | (rgb2rgb_coeff[0][0] << 18) |
519
(rgb2rgb_coeff[1][1] << 9) | rgb2rgb_coeff[2][2];
520
/* scale = 2, sat = 0 */
521
param[1] = (rgb2rgb_coeff[3][0] >> 5) | (2UL << 8);
523
_ipu_write_param_mem(address, param, 2);
526
"addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
527
address, param[0], param[1]);
530
(rgb2rgb_coeff[3][1] << 27) | (rgb2rgb_coeff[0][1] << 18) |
531
(rgb2rgb_coeff[1][0] << 9) | rgb2rgb_coeff[2][0];
532
param[1] = (rgb2rgb_coeff[3][1] >> 5);
535
_ipu_write_param_mem(address, param, 2);
538
"addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
539
address, param[0], param[1]);
542
(rgb2rgb_coeff[3][2] << 27) | (rgb2rgb_coeff[0][2] << 18) |
543
(rgb2rgb_coeff[1][2] << 9) | rgb2rgb_coeff[2][1];
544
param[1] = (rgb2rgb_coeff[3][2] >> 5);
547
_ipu_write_param_mem(address, param, 2);
550
"addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
551
address, param[0], param[1]);
553
dev_err(g_ipu_dev, "Unsupported color space conversion\n");
557
static bool _calc_resize_coeffs(uint32_t inSize, uint32_t outSize,
558
uint32_t * resizeCoeff,
559
uint32_t * downsizeCoeff)
562
uint32_t tempDownsize;
564
/* Cannot downsize more than 8:1 */
565
if ((outSize << 3) < inSize) {
568
/* compute downsizing coefficient */
571
while ((tempSize >= outSize * 2) && (tempDownsize < 2)) {
575
*downsizeCoeff = tempDownsize;
577
/* compute resizing coefficient using the following equation:
578
resizeCoeff = M*(SI -1)/(SO - 1)
579
where M = 2^13, SI - input size, SO - output size */
580
*resizeCoeff = (8192L * (tempSize - 1)) / (outSize - 1);
581
if (*resizeCoeff >= 16384L) {
582
dev_err(g_ipu_dev, "Warning! Overflow on resize coeff.\n");
583
*resizeCoeff = 0x3FFF;
586
dev_dbg(g_ipu_dev, "resizing from %u -> %u pixels, "
587
"downsize=%u, resize=%u.%lu (reg=%u)\n", inSize, outSize,
588
*downsizeCoeff, (*resizeCoeff >= 8192L) ? 1 : 0,
589
((*resizeCoeff & 0x1FFF) * 10000L) / 8192L, *resizeCoeff);