ipu-ic.c 19.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
/*
 * Copyright (C) 2012-2014 Mentor Graphics Inc.
 * Copyright 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved.
 *
 * The code contained herein is licensed under the GNU General Public
 * License. You may obtain a copy of the GNU General Public License
 * Version 2 or later at the following locations:
 *
 * http://www.opensource.org/licenses/gpl-license.html
 * http://www.gnu.org/copyleft/gpl.html
 */

#include <linux/types.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/spinlock.h>
#include <linux/bitrev.h>
#include <linux/io.h>
#include <linux/err.h>
#include "ipu-prv.h"

/* IC Register Offsets */
#define IC_CONF                 0x0000
#define IC_PRP_ENC_RSC          0x0004
#define IC_PRP_VF_RSC           0x0008
#define IC_PP_RSC               0x000C
#define IC_CMBP_1               0x0010
#define IC_CMBP_2               0x0014
#define IC_IDMAC_1              0x0018
#define IC_IDMAC_2              0x001C
#define IC_IDMAC_3              0x0020
#define IC_IDMAC_4              0x0024

/* IC Register Fields */
#define IC_CONF_PRPENC_EN       (1 << 0)
#define IC_CONF_PRPENC_CSC1     (1 << 1)
#define IC_CONF_PRPENC_ROT_EN   (1 << 2)
#define IC_CONF_PRPVF_EN        (1 << 8)
#define IC_CONF_PRPVF_CSC1      (1 << 9)
#define IC_CONF_PRPVF_CSC2      (1 << 10)
#define IC_CONF_PRPVF_CMB       (1 << 11)
#define IC_CONF_PRPVF_ROT_EN    (1 << 12)
#define IC_CONF_PP_EN           (1 << 16)
#define IC_CONF_PP_CSC1         (1 << 17)
#define IC_CONF_PP_CSC2         (1 << 18)
#define IC_CONF_PP_CMB          (1 << 19)
#define IC_CONF_PP_ROT_EN       (1 << 20)
#define IC_CONF_IC_GLB_LOC_A    (1 << 28)
#define IC_CONF_KEY_COLOR_EN    (1 << 29)
#define IC_CONF_RWS_EN          (1 << 30)
#define IC_CONF_CSI_MEM_WR_EN   (1 << 31)

#define IC_IDMAC_1_CB0_BURST_16         (1 << 0)
#define IC_IDMAC_1_CB1_BURST_16         (1 << 1)
#define IC_IDMAC_1_CB2_BURST_16         (1 << 2)
#define IC_IDMAC_1_CB3_BURST_16         (1 << 3)
#define IC_IDMAC_1_CB4_BURST_16         (1 << 4)
#define IC_IDMAC_1_CB5_BURST_16         (1 << 5)
#define IC_IDMAC_1_CB6_BURST_16         (1 << 6)
#define IC_IDMAC_1_CB7_BURST_16         (1 << 7)
#define IC_IDMAC_1_PRPENC_ROT_MASK      (0x7 << 11)
#define IC_IDMAC_1_PRPENC_ROT_OFFSET    11
#define IC_IDMAC_1_PRPVF_ROT_MASK       (0x7 << 14)
#define IC_IDMAC_1_PRPVF_ROT_OFFSET     14
#define IC_IDMAC_1_PP_ROT_MASK          (0x7 << 17)
#define IC_IDMAC_1_PP_ROT_OFFSET        17
#define IC_IDMAC_1_PP_FLIP_RS           (1 << 22)
#define IC_IDMAC_1_PRPVF_FLIP_RS        (1 << 21)
#define IC_IDMAC_1_PRPENC_FLIP_RS       (1 << 20)

#define IC_IDMAC_2_PRPENC_HEIGHT_MASK   (0x3ff << 0)
#define IC_IDMAC_2_PRPENC_HEIGHT_OFFSET 0
#define IC_IDMAC_2_PRPVF_HEIGHT_MASK    (0x3ff << 10)
#define IC_IDMAC_2_PRPVF_HEIGHT_OFFSET  10
#define IC_IDMAC_2_PP_HEIGHT_MASK       (0x3ff << 20)
#define IC_IDMAC_2_PP_HEIGHT_OFFSET     20

#define IC_IDMAC_3_PRPENC_WIDTH_MASK    (0x3ff << 0)
#define IC_IDMAC_3_PRPENC_WIDTH_OFFSET  0
#define IC_IDMAC_3_PRPVF_WIDTH_MASK     (0x3ff << 10)
#define IC_IDMAC_3_PRPVF_WIDTH_OFFSET   10
#define IC_IDMAC_3_PP_WIDTH_MASK        (0x3ff << 20)
#define IC_IDMAC_3_PP_WIDTH_OFFSET      20

struct ic_task_regoffs {
	u32 rsc;
	u32 tpmem_csc[2];
};

struct ic_task_bitfields {
	u32 ic_conf_en;
	u32 ic_conf_rot_en;
	u32 ic_conf_cmb_en;
	u32 ic_conf_csc1_en;
	u32 ic_conf_csc2_en;
	u32 ic_cmb_galpha_bit;
};

static const struct ic_task_regoffs ic_task_reg[IC_NUM_TASKS] = {
	[IC_TASK_ENCODER] = {
		.rsc = IC_PRP_ENC_RSC,
		.tpmem_csc = {0x2008, 0},
	},
	[IC_TASK_VIEWFINDER] = {
		.rsc = IC_PRP_VF_RSC,
		.tpmem_csc = {0x4028, 0x4040},
	},
	[IC_TASK_POST_PROCESSOR] = {
		.rsc = IC_PP_RSC,
		.tpmem_csc = {0x6060, 0x6078},
	},
};

static const struct ic_task_bitfields ic_task_bit[IC_NUM_TASKS] = {
	[IC_TASK_ENCODER] = {
		.ic_conf_en = IC_CONF_PRPENC_EN,
		.ic_conf_rot_en = IC_CONF_PRPENC_ROT_EN,
		.ic_conf_cmb_en = 0,    /* NA */
		.ic_conf_csc1_en = IC_CONF_PRPENC_CSC1,
		.ic_conf_csc2_en = 0,   /* NA */
		.ic_cmb_galpha_bit = 0, /* NA */
	},
	[IC_TASK_VIEWFINDER] = {
		.ic_conf_en = IC_CONF_PRPVF_EN,
		.ic_conf_rot_en = IC_CONF_PRPVF_ROT_EN,
		.ic_conf_cmb_en = IC_CONF_PRPVF_CMB,
		.ic_conf_csc1_en = IC_CONF_PRPVF_CSC1,
		.ic_conf_csc2_en = IC_CONF_PRPVF_CSC2,
		.ic_cmb_galpha_bit = 0,
	},
	[IC_TASK_POST_PROCESSOR] = {
		.ic_conf_en = IC_CONF_PP_EN,
		.ic_conf_rot_en = IC_CONF_PP_ROT_EN,
		.ic_conf_cmb_en = IC_CONF_PP_CMB,
		.ic_conf_csc1_en = IC_CONF_PP_CSC1,
		.ic_conf_csc2_en = IC_CONF_PP_CSC2,
		.ic_cmb_galpha_bit = 8,
	},
};

struct ipu_ic_priv;

struct ipu_ic {
	enum ipu_ic_task task;
	const struct ic_task_regoffs *reg;
	const struct ic_task_bitfields *bit;

	enum ipu_color_space in_cs, g_in_cs;
	enum ipu_color_space out_cs;
	bool graphics;
	bool rotation;
	bool in_use;

	struct ipu_ic_priv *priv;
};

struct ipu_ic_priv {
	void __iomem *base;
	void __iomem *tpmem_base;
	spinlock_t lock;
	struct ipu_soc *ipu;
	int use_count;
	struct ipu_ic task[IC_NUM_TASKS];
};

static inline u32 ipu_ic_read(struct ipu_ic *ic, unsigned offset)
{
	return readl(ic->priv->base + offset);
}

static inline void ipu_ic_write(struct ipu_ic *ic, u32 value, unsigned offset)
{
	writel(value, ic->priv->base + offset);
}

struct ic_csc_params {
	s16 coeff[3][3];	/* signed 9-bit integer coefficients */
	s16 offset[3];		/* signed 11+2-bit fixed point offset */
	u8 scale:2;		/* scale coefficients * 2^(scale-1) */
	bool sat:1;		/* saturate to (16, 235(Y) / 240(U, V)) */
};

/*
 * Y = R *  .299 + G *  .587 + B *  .114;
 * U = R * -.169 + G * -.332 + B *  .500 + 128.;
 * V = R *  .500 + G * -.419 + B * -.0813 + 128.;
 */
static const struct ic_csc_params ic_csc_rgb2ycbcr = {
	.coeff = {
		{ 77, 150, 29 },
		{ 469, 427, 128 },
		{ 128, 405, 491 },
	},
	.offset = { 0, 512, 512 },
	.scale = 1,
};

/* transparent RGB->RGB matrix for graphics combining */
static const struct ic_csc_params ic_csc_rgb2rgb = {
	.coeff = {
		{ 128, 0, 0 },
		{ 0, 128, 0 },
		{ 0, 0, 128 },
	},
	.scale = 2,
};

/*
 * R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128));
 * G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128));
 * B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128);
 */
static const struct ic_csc_params ic_csc_ycbcr2rgb = {
	.coeff = {
		{ 149, 0, 204 },
		{ 149, 462, 408 },
		{ 149, 255, 0 },
	},
	.offset = { -446, 266, -554 },
	.scale = 2,
};

static int init_csc(struct ipu_ic *ic,
		    enum ipu_color_space inf,
		    enum ipu_color_space outf,
		    int csc_index)
{
	struct ipu_ic_priv *priv = ic->priv;
	const struct ic_csc_params *params;
	u32 __iomem *base;
	const u16 (*c)[3];
	const u16 *a;
	u32 param;

	base = (u32 __iomem *)
		(priv->tpmem_base + ic->reg->tpmem_csc[csc_index]);

	if (inf == IPUV3_COLORSPACE_YUV && outf == IPUV3_COLORSPACE_RGB)
		params = &ic_csc_ycbcr2rgb;
	else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_YUV)
		params = &ic_csc_rgb2ycbcr;
	else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_RGB)
		params = &ic_csc_rgb2rgb;
	else {
		dev_err(priv->ipu->dev, "Unsupported color space conversion\n");
		return -EINVAL;
	}

	/* Cast to unsigned */
	c = (const u16 (*)[3])params->coeff;
	a = (const u16 *)params->offset;

	param = ((a[0] & 0x1f) << 27) | ((c[0][0] & 0x1ff) << 18) |
		((c[1][1] & 0x1ff) << 9) | (c[2][2] & 0x1ff);
	writel(param, base++);

	param = ((a[0] & 0x1fe0) >> 5) | (params->scale << 8) |
		(params->sat << 9);
	writel(param, base++);

	param = ((a[1] & 0x1f) << 27) | ((c[0][1] & 0x1ff) << 18) |
		((c[1][0] & 0x1ff) << 9) | (c[2][0] & 0x1ff);
	writel(param, base++);

	param = ((a[1] & 0x1fe0) >> 5);
	writel(param, base++);

	param = ((a[2] & 0x1f) << 27) | ((c[0][2] & 0x1ff) << 18) |
		((c[1][2] & 0x1ff) << 9) | (c[2][1] & 0x1ff);
	writel(param, base++);

	param = ((a[2] & 0x1fe0) >> 5);
	writel(param, base++);

	return 0;
}

static int calc_resize_coeffs(struct ipu_ic *ic,
			      u32 in_size, u32 out_size,
			      u32 *resize_coeff,
			      u32 *downsize_coeff)
{
	struct ipu_ic_priv *priv = ic->priv;
	struct ipu_soc *ipu = priv->ipu;
	u32 temp_size, temp_downsize;

	/*
	 * Input size cannot be more than 4096, and output size cannot
	 * be more than 1024
	 */
	if (in_size > 4096) {
		dev_err(ipu->dev, "Unsupported resize (in_size > 4096)\n");
		return -EINVAL;
	}
	if (out_size > 1024) {
		dev_err(ipu->dev, "Unsupported resize (out_size > 1024)\n");
		return -EINVAL;
	}

	/* Cannot downsize more than 8:1 */
	if ((out_size << 3) < in_size) {
		dev_err(ipu->dev, "Unsupported downsize\n");
		return -EINVAL;
	}

	/* Compute downsizing coefficient */
	temp_downsize = 0;
	temp_size = in_size;
	while (((temp_size > 1024) || (temp_size >= out_size * 2)) &&
	       (temp_downsize < 2)) {
		temp_size >>= 1;
		temp_downsize++;
	}
	*downsize_coeff = temp_downsize;

	/*
	 * compute resizing coefficient using the following equation:
	 * resize_coeff = M * (SI - 1) / (SO - 1)
	 * where M = 2^13, SI = input size, SO = output size
	 */
	*resize_coeff = (8192L * (temp_size - 1)) / (out_size - 1);
	if (*resize_coeff >= 16384L) {
		dev_err(ipu->dev, "Warning! Overflow on resize coeff.\n");
		*resize_coeff = 0x3FFF;
	}

	return 0;
}

void ipu_ic_task_enable(struct ipu_ic *ic)
{
	struct ipu_ic_priv *priv = ic->priv;
	unsigned long flags;
	u32 ic_conf;

	spin_lock_irqsave(&priv->lock, flags);

	ic_conf = ipu_ic_read(ic, IC_CONF);

	ic_conf |= ic->bit->ic_conf_en;

	if (ic->rotation)
		ic_conf |= ic->bit->ic_conf_rot_en;

	if (ic->in_cs != ic->out_cs)
		ic_conf |= ic->bit->ic_conf_csc1_en;

	if (ic->graphics) {
		ic_conf |= ic->bit->ic_conf_cmb_en;
		ic_conf |= ic->bit->ic_conf_csc1_en;

		if (ic->g_in_cs != ic->out_cs)
			ic_conf |= ic->bit->ic_conf_csc2_en;
	}

	ipu_ic_write(ic, ic_conf, IC_CONF);

	spin_unlock_irqrestore(&priv->lock, flags);
}
EXPORT_SYMBOL_GPL(ipu_ic_task_enable);

void ipu_ic_task_disable(struct ipu_ic *ic)
{
	struct ipu_ic_priv *priv = ic->priv;
	unsigned long flags;
	u32 ic_conf;

	spin_lock_irqsave(&priv->lock, flags);

	ic_conf = ipu_ic_read(ic, IC_CONF);

	ic_conf &= ~(ic->bit->ic_conf_en |
		     ic->bit->ic_conf_csc1_en |
		     ic->bit->ic_conf_rot_en);
	if (ic->bit->ic_conf_csc2_en)
		ic_conf &= ~ic->bit->ic_conf_csc2_en;
	if (ic->bit->ic_conf_cmb_en)
		ic_conf &= ~ic->bit->ic_conf_cmb_en;

	ipu_ic_write(ic, ic_conf, IC_CONF);

	ic->rotation = ic->graphics = false;

	spin_unlock_irqrestore(&priv->lock, flags);
}
EXPORT_SYMBOL_GPL(ipu_ic_task_disable);

int ipu_ic_task_graphics_init(struct ipu_ic *ic,
			      enum ipu_color_space in_g_cs,
			      bool galpha_en, u32 galpha,
			      bool colorkey_en, u32 colorkey)
{
	struct ipu_ic_priv *priv = ic->priv;
	unsigned long flags;
	u32 reg, ic_conf;
	int ret = 0;

	if (ic->task == IC_TASK_ENCODER)
		return -EINVAL;

	spin_lock_irqsave(&priv->lock, flags);

	ic_conf = ipu_ic_read(ic, IC_CONF);

	if (!(ic_conf & ic->bit->ic_conf_csc1_en)) {
		/* need transparent CSC1 conversion */
		ret = init_csc(ic, IPUV3_COLORSPACE_RGB,
			       IPUV3_COLORSPACE_RGB, 0);
		if (ret)
			goto unlock;
	}

	ic->g_in_cs = in_g_cs;

	if (ic->g_in_cs != ic->out_cs) {
		ret = init_csc(ic, ic->g_in_cs, ic->out_cs, 1);
		if (ret)
			goto unlock;
	}

	if (galpha_en) {
		ic_conf |= IC_CONF_IC_GLB_LOC_A;
		reg = ipu_ic_read(ic, IC_CMBP_1);
		reg &= ~(0xff << ic->bit->ic_cmb_galpha_bit);
		reg |= (galpha << ic->bit->ic_cmb_galpha_bit);
		ipu_ic_write(ic, reg, IC_CMBP_1);
	} else
		ic_conf &= ~IC_CONF_IC_GLB_LOC_A;

	if (colorkey_en) {
		ic_conf |= IC_CONF_KEY_COLOR_EN;
		ipu_ic_write(ic, colorkey, IC_CMBP_2);
	} else
		ic_conf &= ~IC_CONF_KEY_COLOR_EN;

	ipu_ic_write(ic, ic_conf, IC_CONF);

	ic->graphics = true;
unlock:
	spin_unlock_irqrestore(&priv->lock, flags);
	return ret;
}
EXPORT_SYMBOL_GPL(ipu_ic_task_graphics_init);

int ipu_ic_task_init(struct ipu_ic *ic,
		     int in_width, int in_height,
		     int out_width, int out_height,
		     enum ipu_color_space in_cs,
		     enum ipu_color_space out_cs)
{
	struct ipu_ic_priv *priv = ic->priv;
	u32 reg, downsize_coeff, resize_coeff;
	unsigned long flags;
	int ret = 0;

	/* Setup vertical resizing */
	ret = calc_resize_coeffs(ic, in_height, out_height,
				 &resize_coeff, &downsize_coeff);
	if (ret)
		return ret;

	reg = (downsize_coeff << 30) | (resize_coeff << 16);

	/* Setup horizontal resizing */
	ret = calc_resize_coeffs(ic, in_width, out_width,
				 &resize_coeff, &downsize_coeff);
	if (ret)
		return ret;

	reg |= (downsize_coeff << 14) | resize_coeff;

	spin_lock_irqsave(&priv->lock, flags);

	ipu_ic_write(ic, reg, ic->reg->rsc);

	/* Setup color space conversion */
	ic->in_cs = in_cs;
	ic->out_cs = out_cs;

	if (ic->in_cs != ic->out_cs) {
		ret = init_csc(ic, ic->in_cs, ic->out_cs, 0);
		if (ret)
			goto unlock;
	}

unlock:
	spin_unlock_irqrestore(&priv->lock, flags);
	return ret;
}
EXPORT_SYMBOL_GPL(ipu_ic_task_init);

int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel,
			  u32 width, u32 height, int burst_size,
			  enum ipu_rotate_mode rot)
{
	struct ipu_ic_priv *priv = ic->priv;
	struct ipu_soc *ipu = priv->ipu;
	u32 ic_idmac_1, ic_idmac_2, ic_idmac_3;
	u32 temp_rot = bitrev8(rot) >> 5;
	bool need_hor_flip = false;
	unsigned long flags;
	int ret = 0;

	if ((burst_size != 8) && (burst_size != 16)) {
		dev_err(ipu->dev, "Illegal burst length for IC\n");
		return -EINVAL;
	}

	width--;
	height--;

	if (temp_rot & 0x2)	/* Need horizontal flip */
		need_hor_flip = true;

	spin_lock_irqsave(&priv->lock, flags);

	ic_idmac_1 = ipu_ic_read(ic, IC_IDMAC_1);
	ic_idmac_2 = ipu_ic_read(ic, IC_IDMAC_2);
	ic_idmac_3 = ipu_ic_read(ic, IC_IDMAC_3);

	switch (channel->num) {
	case IPUV3_CHANNEL_IC_PP_MEM:
		if (burst_size == 16)
			ic_idmac_1 |= IC_IDMAC_1_CB2_BURST_16;
		else
			ic_idmac_1 &= ~IC_IDMAC_1_CB2_BURST_16;

		if (need_hor_flip)
			ic_idmac_1 |= IC_IDMAC_1_PP_FLIP_RS;
		else
			ic_idmac_1 &= ~IC_IDMAC_1_PP_FLIP_RS;

		ic_idmac_2 &= ~IC_IDMAC_2_PP_HEIGHT_MASK;
		ic_idmac_2 |= height << IC_IDMAC_2_PP_HEIGHT_OFFSET;

		ic_idmac_3 &= ~IC_IDMAC_3_PP_WIDTH_MASK;
		ic_idmac_3 |= width << IC_IDMAC_3_PP_WIDTH_OFFSET;
		break;
	case IPUV3_CHANNEL_MEM_IC_PP:
		if (burst_size == 16)
			ic_idmac_1 |= IC_IDMAC_1_CB5_BURST_16;
		else
			ic_idmac_1 &= ~IC_IDMAC_1_CB5_BURST_16;
		break;
	case IPUV3_CHANNEL_MEM_ROT_PP:
		ic_idmac_1 &= ~IC_IDMAC_1_PP_ROT_MASK;
		ic_idmac_1 |= temp_rot << IC_IDMAC_1_PP_ROT_OFFSET;
		break;
	case IPUV3_CHANNEL_MEM_IC_PRP_VF:
		if (burst_size == 16)
			ic_idmac_1 |= IC_IDMAC_1_CB6_BURST_16;
		else
			ic_idmac_1 &= ~IC_IDMAC_1_CB6_BURST_16;
		break;
	case IPUV3_CHANNEL_IC_PRP_ENC_MEM:
		if (burst_size == 16)
			ic_idmac_1 |= IC_IDMAC_1_CB0_BURST_16;
		else
			ic_idmac_1 &= ~IC_IDMAC_1_CB0_BURST_16;

		if (need_hor_flip)
			ic_idmac_1 |= IC_IDMAC_1_PRPENC_FLIP_RS;
		else
			ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_FLIP_RS;

		ic_idmac_2 &= ~IC_IDMAC_2_PRPENC_HEIGHT_MASK;
		ic_idmac_2 |= height << IC_IDMAC_2_PRPENC_HEIGHT_OFFSET;

		ic_idmac_3 &= ~IC_IDMAC_3_PRPENC_WIDTH_MASK;
		ic_idmac_3 |= width << IC_IDMAC_3_PRPENC_WIDTH_OFFSET;
		break;
	case IPUV3_CHANNEL_MEM_ROT_ENC:
		ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_ROT_MASK;
		ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPENC_ROT_OFFSET;
		break;
	case IPUV3_CHANNEL_IC_PRP_VF_MEM:
		if (burst_size == 16)
			ic_idmac_1 |= IC_IDMAC_1_CB1_BURST_16;
		else
			ic_idmac_1 &= ~IC_IDMAC_1_CB1_BURST_16;

		if (need_hor_flip)
			ic_idmac_1 |= IC_IDMAC_1_PRPVF_FLIP_RS;
		else
			ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_FLIP_RS;

		ic_idmac_2 &= ~IC_IDMAC_2_PRPVF_HEIGHT_MASK;
		ic_idmac_2 |= height << IC_IDMAC_2_PRPVF_HEIGHT_OFFSET;

		ic_idmac_3 &= ~IC_IDMAC_3_PRPVF_WIDTH_MASK;
		ic_idmac_3 |= width << IC_IDMAC_3_PRPVF_WIDTH_OFFSET;
		break;
	case IPUV3_CHANNEL_MEM_ROT_VF:
		ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_ROT_MASK;
		ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPVF_ROT_OFFSET;
		break;
	case IPUV3_CHANNEL_G_MEM_IC_PRP_VF:
		if (burst_size == 16)
			ic_idmac_1 |= IC_IDMAC_1_CB3_BURST_16;
		else
			ic_idmac_1 &= ~IC_IDMAC_1_CB3_BURST_16;
		break;
	case IPUV3_CHANNEL_G_MEM_IC_PP:
		if (burst_size == 16)
			ic_idmac_1 |= IC_IDMAC_1_CB4_BURST_16;
		else
			ic_idmac_1 &= ~IC_IDMAC_1_CB4_BURST_16;
		break;
	case IPUV3_CHANNEL_VDI_MEM_IC_VF:
		if (burst_size == 16)
			ic_idmac_1 |= IC_IDMAC_1_CB7_BURST_16;
		else
			ic_idmac_1 &= ~IC_IDMAC_1_CB7_BURST_16;
		break;
	default:
		goto unlock;
	}

	ipu_ic_write(ic, ic_idmac_1, IC_IDMAC_1);
	ipu_ic_write(ic, ic_idmac_2, IC_IDMAC_2);
	ipu_ic_write(ic, ic_idmac_3, IC_IDMAC_3);

	if (rot >= IPU_ROTATE_90_RIGHT)
		ic->rotation = true;

unlock:
	spin_unlock_irqrestore(&priv->lock, flags);
	return ret;
}
EXPORT_SYMBOL_GPL(ipu_ic_task_idma_init);

int ipu_ic_enable(struct ipu_ic *ic)
{
	struct ipu_ic_priv *priv = ic->priv;
	unsigned long flags;
	u32 module = IPU_CONF_IC_EN;

	spin_lock_irqsave(&priv->lock, flags);

	if (ic->rotation)
		module |= IPU_CONF_ROT_EN;

	if (!priv->use_count)
		ipu_module_enable(priv->ipu, module);

	priv->use_count++;

	spin_unlock_irqrestore(&priv->lock, flags);

	return 0;
}
EXPORT_SYMBOL_GPL(ipu_ic_enable);

int ipu_ic_disable(struct ipu_ic *ic)
{
	struct ipu_ic_priv *priv = ic->priv;
	unsigned long flags;
	u32 module = IPU_CONF_IC_EN | IPU_CONF_ROT_EN;

	spin_lock_irqsave(&priv->lock, flags);

	priv->use_count--;

	if (!priv->use_count)
		ipu_module_disable(priv->ipu, module);

	if (priv->use_count < 0)
		priv->use_count = 0;

	spin_unlock_irqrestore(&priv->lock, flags);

	return 0;
}
EXPORT_SYMBOL_GPL(ipu_ic_disable);

struct ipu_ic *ipu_ic_get(struct ipu_soc *ipu, enum ipu_ic_task task)
{
	struct ipu_ic_priv *priv = ipu->ic_priv;
	unsigned long flags;
	struct ipu_ic *ic, *ret;

	if (task >= IC_NUM_TASKS)
		return ERR_PTR(-EINVAL);

	ic = &priv->task[task];

	spin_lock_irqsave(&priv->lock, flags);

	if (ic->in_use) {
		ret = ERR_PTR(-EBUSY);
		goto unlock;
	}

	ic->in_use = true;
	ret = ic;

unlock:
	spin_unlock_irqrestore(&priv->lock, flags);
	return ret;
}
EXPORT_SYMBOL_GPL(ipu_ic_get);

void ipu_ic_put(struct ipu_ic *ic)
{
	struct ipu_ic_priv *priv = ic->priv;
	unsigned long flags;

	spin_lock_irqsave(&priv->lock, flags);
	ic->in_use = false;
	spin_unlock_irqrestore(&priv->lock, flags);
}
EXPORT_SYMBOL_GPL(ipu_ic_put);

int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
		unsigned long base, unsigned long tpmem_base)
{
	struct ipu_ic_priv *priv;
	int i;

	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	ipu->ic_priv = priv;

	spin_lock_init(&priv->lock);
	priv->base = devm_ioremap(dev, base, PAGE_SIZE);
	if (!priv->base)
		return -ENOMEM;
	priv->tpmem_base = devm_ioremap(dev, tpmem_base, SZ_64K);
	if (!priv->tpmem_base)
		return -ENOMEM;

	dev_dbg(dev, "IC base: 0x%08lx remapped to %p\n", base, priv->base);

	priv->ipu = ipu;

	for (i = 0; i < IC_NUM_TASKS; i++) {
		priv->task[i].task = i;
		priv->task[i].priv = priv;
		priv->task[i].reg = &ic_task_reg[i];
		priv->task[i].bit = &ic_task_bit[i];
	}

	return 0;
}

void ipu_ic_exit(struct ipu_soc *ipu)
{
}

void ipu_ic_dump(struct ipu_ic *ic)
{
	struct ipu_ic_priv *priv = ic->priv;
	struct ipu_soc *ipu = priv->ipu;

	dev_dbg(ipu->dev, "IC_CONF = \t0x%08X\n",
		ipu_ic_read(ic, IC_CONF));
	dev_dbg(ipu->dev, "IC_PRP_ENC_RSC = \t0x%08X\n",
		ipu_ic_read(ic, IC_PRP_ENC_RSC));
	dev_dbg(ipu->dev, "IC_PRP_VF_RSC = \t0x%08X\n",
		ipu_ic_read(ic, IC_PRP_VF_RSC));
	dev_dbg(ipu->dev, "IC_PP_RSC = \t0x%08X\n",
		ipu_ic_read(ic, IC_PP_RSC));
	dev_dbg(ipu->dev, "IC_CMBP_1 = \t0x%08X\n",
		ipu_ic_read(ic, IC_CMBP_1));
	dev_dbg(ipu->dev, "IC_CMBP_2 = \t0x%08X\n",
		ipu_ic_read(ic, IC_CMBP_2));
	dev_dbg(ipu->dev, "IC_IDMAC_1 = \t0x%08X\n",
		ipu_ic_read(ic, IC_IDMAC_1));
	dev_dbg(ipu->dev, "IC_IDMAC_2 = \t0x%08X\n",
		ipu_ic_read(ic, IC_IDMAC_2));
	dev_dbg(ipu->dev, "IC_IDMAC_3 = \t0x%08X\n",
		ipu_ic_read(ic, IC_IDMAC_3));
	dev_dbg(ipu->dev, "IC_IDMAC_4 = \t0x%08X\n",
		ipu_ic_read(ic, IC_IDMAC_4));
}
EXPORT_SYMBOL_GPL(ipu_ic_dump);