750 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>4 * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.5 */6#include <linux/export.h>7#include <linux/module.h>8#include <linux/types.h>9#include <linux/errno.h>10#include <linux/io.h>11#include <linux/err.h>12#include <linux/platform_device.h>13 14#include <video/imx-ipu-v3.h>15#include "ipu-prv.h"16 17struct ipu_di {18 void __iomem *base;19 int id;20 u32 module;21 struct clk *clk_di; /* display input clock */22 struct clk *clk_ipu; /* IPU bus clock */23 struct clk *clk_di_pixel; /* resulting pixel clock */24 bool inuse;25 struct ipu_soc *ipu;26};27 28static DEFINE_MUTEX(di_mutex);29 30struct di_sync_config {31 int run_count;32 int run_src;33 int offset_count;34 int offset_src;35 int repeat_count;36 int cnt_clr_src;37 int cnt_polarity_gen_en;38 int cnt_polarity_clr_src;39 int cnt_polarity_trigger_src;40 int cnt_up;41 int cnt_down;42};43 44enum di_pins {45 DI_PIN11 = 0,46 DI_PIN12 = 1,47 DI_PIN13 = 2,48 DI_PIN14 = 3,49 DI_PIN15 = 4,50 DI_PIN16 = 5,51 DI_PIN17 = 6,52 DI_PIN_CS = 7,53 54 DI_PIN_SER_CLK = 0,55 DI_PIN_SER_RS = 1,56};57 58enum di_sync_wave {59 DI_SYNC_NONE = 0,60 DI_SYNC_CLK = 1,61 DI_SYNC_INT_HSYNC = 2,62 DI_SYNC_HSYNC = 3,63 DI_SYNC_VSYNC = 4,64 DI_SYNC_DE = 6,65 66 DI_SYNC_CNT1 = 2, /* counter >= 2 only */67 DI_SYNC_CNT4 = 5, /* counter >= 5 only */68 DI_SYNC_CNT5 = 6, /* counter >= 6 only */69};70 71#define SYNC_WAVE 072 73#define DI_GENERAL 0x000074#define DI_BS_CLKGEN0 0x000475#define DI_BS_CLKGEN1 0x000876#define DI_SW_GEN0(gen) (0x000c + 4 * ((gen) - 1))77#define DI_SW_GEN1(gen) (0x0030 + 4 * ((gen) - 1))78#define DI_STP_REP(gen) (0x0148 + 4 * (((gen) - 1)/2))79#define DI_SYNC_AS_GEN 0x005480#define DI_DW_GEN(gen) (0x0058 + 4 * (gen))81#define DI_DW_SET(gen, set) (0x0088 + 4 * ((gen) + 0xc * (set)))82#define DI_SER_CONF 0x015c83#define DI_SSC 0x016084#define DI_POL 0x016485#define DI_AW0 0x016886#define DI_AW1 0x016c87#define DI_SCR_CONF 0x017088#define DI_STAT 0x017489 90#define DI_SW_GEN0_RUN_COUNT(x) ((x) << 19)91#define DI_SW_GEN0_RUN_SRC(x) ((x) << 16)92#define DI_SW_GEN0_OFFSET_COUNT(x) ((x) << 3)93#define DI_SW_GEN0_OFFSET_SRC(x) ((x) << 0)94 95#define DI_SW_GEN1_CNT_POL_GEN_EN(x) ((x) << 29)96#define DI_SW_GEN1_CNT_CLR_SRC(x) ((x) << 25)97#define DI_SW_GEN1_CNT_POL_TRIGGER_SRC(x) ((x) << 12)98#define DI_SW_GEN1_CNT_POL_CLR_SRC(x) ((x) << 9)99#define DI_SW_GEN1_CNT_DOWN(x) ((x) << 16)100#define DI_SW_GEN1_CNT_UP(x) (x)101#define DI_SW_GEN1_AUTO_RELOAD (0x10000000)102 103#define DI_DW_GEN_ACCESS_SIZE_OFFSET 24104#define DI_DW_GEN_COMPONENT_SIZE_OFFSET 16105 106#define DI_GEN_POLARITY_1 (1 << 0)107#define DI_GEN_POLARITY_2 (1 << 1)108#define DI_GEN_POLARITY_3 (1 << 2)109#define DI_GEN_POLARITY_4 (1 << 3)110#define DI_GEN_POLARITY_5 (1 << 4)111#define DI_GEN_POLARITY_6 (1 << 5)112#define DI_GEN_POLARITY_7 (1 << 6)113#define DI_GEN_POLARITY_8 (1 << 7)114#define DI_GEN_POLARITY_DISP_CLK (1 << 17)115#define DI_GEN_DI_CLK_EXT (1 << 20)116#define DI_GEN_DI_VSYNC_EXT (1 << 21)117 118#define DI_POL_DRDY_DATA_POLARITY (1 << 7)119#define DI_POL_DRDY_POLARITY_15 (1 << 4)120 121#define DI_VSYNC_SEL_OFFSET 13122 123static inline u32 ipu_di_read(struct ipu_di *di, unsigned offset)124{125 return readl(di->base + offset);126}127 128static inline void ipu_di_write(struct ipu_di *di, u32 value, unsigned offset)129{130 writel(value, di->base + offset);131}132 133static void ipu_di_data_wave_config(struct ipu_di *di,134 int wave_gen,135 int access_size, int component_size)136{137 u32 reg;138 reg = (access_size << DI_DW_GEN_ACCESS_SIZE_OFFSET) |139 (component_size << DI_DW_GEN_COMPONENT_SIZE_OFFSET);140 ipu_di_write(di, reg, DI_DW_GEN(wave_gen));141}142 143static void ipu_di_data_pin_config(struct ipu_di *di, int wave_gen, int di_pin,144 int set, int up, int down)145{146 u32 reg;147 148 reg = ipu_di_read(di, DI_DW_GEN(wave_gen));149 reg &= ~(0x3 << (di_pin * 2));150 reg |= set << (di_pin * 2);151 ipu_di_write(di, reg, DI_DW_GEN(wave_gen));152 153 ipu_di_write(di, (down << 16) | up, DI_DW_SET(wave_gen, set));154}155 156static void ipu_di_sync_config(struct ipu_di *di, struct di_sync_config *config,157 int start, int count)158{159 u32 reg;160 int i;161 162 for (i = 0; i < count; i++) {163 struct di_sync_config *c = &config[i];164 int wave_gen = start + i + 1;165 166 if ((c->run_count >= 0x1000) || (c->offset_count >= 0x1000) ||167 (c->repeat_count >= 0x1000) ||168 (c->cnt_up >= 0x400) ||169 (c->cnt_down >= 0x400)) {170 dev_err(di->ipu->dev, "DI%d counters out of range.\n",171 di->id);172 return;173 }174 175 reg = DI_SW_GEN0_RUN_COUNT(c->run_count) |176 DI_SW_GEN0_RUN_SRC(c->run_src) |177 DI_SW_GEN0_OFFSET_COUNT(c->offset_count) |178 DI_SW_GEN0_OFFSET_SRC(c->offset_src);179 ipu_di_write(di, reg, DI_SW_GEN0(wave_gen));180 181 reg = DI_SW_GEN1_CNT_POL_GEN_EN(c->cnt_polarity_gen_en) |182 DI_SW_GEN1_CNT_CLR_SRC(c->cnt_clr_src) |183 DI_SW_GEN1_CNT_POL_TRIGGER_SRC(184 c->cnt_polarity_trigger_src) |185 DI_SW_GEN1_CNT_POL_CLR_SRC(c->cnt_polarity_clr_src) |186 DI_SW_GEN1_CNT_DOWN(c->cnt_down) |187 DI_SW_GEN1_CNT_UP(c->cnt_up);188 189 /* Enable auto reload */190 if (c->repeat_count == 0)191 reg |= DI_SW_GEN1_AUTO_RELOAD;192 193 ipu_di_write(di, reg, DI_SW_GEN1(wave_gen));194 195 reg = ipu_di_read(di, DI_STP_REP(wave_gen));196 reg &= ~(0xffff << (16 * ((wave_gen - 1) & 0x1)));197 reg |= c->repeat_count << (16 * ((wave_gen - 1) & 0x1));198 ipu_di_write(di, reg, DI_STP_REP(wave_gen));199 }200}201 202static void ipu_di_sync_config_interlaced(struct ipu_di *di,203 struct ipu_di_signal_cfg *sig)204{205 u32 h_total = sig->mode.hactive + sig->mode.hsync_len +206 sig->mode.hback_porch + sig->mode.hfront_porch;207 u32 v_total = sig->mode.vactive + sig->mode.vsync_len +208 sig->mode.vback_porch + sig->mode.vfront_porch;209 struct di_sync_config cfg[] = {210 {211 /* 1: internal VSYNC for each frame */212 .run_count = v_total * 2 - 1,213 .run_src = 3, /* == counter 7 */214 }, {215 /* PIN2: HSYNC waveform */216 .run_count = h_total - 1,217 .run_src = DI_SYNC_CLK,218 .cnt_polarity_gen_en = 1,219 .cnt_polarity_trigger_src = DI_SYNC_CLK,220 .cnt_down = sig->mode.hsync_len * 2,221 }, {222 /* PIN3: VSYNC waveform */223 .run_count = v_total - 1,224 .run_src = 4, /* == counter 7 */225 .cnt_polarity_gen_en = 1,226 .cnt_polarity_trigger_src = 4, /* == counter 7 */227 .cnt_down = sig->mode.vsync_len * 2,228 .cnt_clr_src = DI_SYNC_CNT1,229 }, {230 /* 4: Field */231 .run_count = v_total / 2,232 .run_src = DI_SYNC_HSYNC,233 .offset_count = h_total / 2,234 .offset_src = DI_SYNC_CLK,235 .repeat_count = 2,236 .cnt_clr_src = DI_SYNC_CNT1,237 }, {238 /* 5: Active lines */239 .run_src = DI_SYNC_HSYNC,240 .offset_count = (sig->mode.vsync_len +241 sig->mode.vback_porch) / 2,242 .offset_src = DI_SYNC_HSYNC,243 .repeat_count = sig->mode.vactive / 2,244 .cnt_clr_src = DI_SYNC_CNT4,245 }, {246 /* 6: Active pixel, referenced by DC */247 .run_src = DI_SYNC_CLK,248 .offset_count = sig->mode.hsync_len +249 sig->mode.hback_porch,250 .offset_src = DI_SYNC_CLK,251 .repeat_count = sig->mode.hactive,252 .cnt_clr_src = DI_SYNC_CNT5,253 }, {254 /* 7: Half line HSYNC */255 .run_count = h_total / 2 - 1,256 .run_src = DI_SYNC_CLK,257 }258 };259 260 ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg));261 262 ipu_di_write(di, v_total / 2 - 1, DI_SCR_CONF);263}264 265static void ipu_di_sync_config_noninterlaced(struct ipu_di *di,266 struct ipu_di_signal_cfg *sig, int div)267{268 u32 h_total = sig->mode.hactive + sig->mode.hsync_len +269 sig->mode.hback_porch + sig->mode.hfront_porch;270 u32 v_total = sig->mode.vactive + sig->mode.vsync_len +271 sig->mode.vback_porch + sig->mode.vfront_porch;272 struct di_sync_config cfg[] = {273 {274 /* 1: INT_HSYNC */275 .run_count = h_total - 1,276 .run_src = DI_SYNC_CLK,277 } , {278 /* PIN2: HSYNC */279 .run_count = h_total - 1,280 .run_src = DI_SYNC_CLK,281 .offset_count = div * sig->v_to_h_sync,282 .offset_src = DI_SYNC_CLK,283 .cnt_polarity_gen_en = 1,284 .cnt_polarity_trigger_src = DI_SYNC_CLK,285 .cnt_down = sig->mode.hsync_len * 2,286 } , {287 /* PIN3: VSYNC */288 .run_count = v_total - 1,289 .run_src = DI_SYNC_INT_HSYNC,290 .cnt_polarity_gen_en = 1,291 .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC,292 .cnt_down = sig->mode.vsync_len * 2,293 } , {294 /* 4: Line Active */295 .run_src = DI_SYNC_HSYNC,296 .offset_count = sig->mode.vsync_len +297 sig->mode.vback_porch,298 .offset_src = DI_SYNC_HSYNC,299 .repeat_count = sig->mode.vactive,300 .cnt_clr_src = DI_SYNC_VSYNC,301 } , {302 /* 5: Pixel Active, referenced by DC */303 .run_src = DI_SYNC_CLK,304 .offset_count = sig->mode.hsync_len +305 sig->mode.hback_porch,306 .offset_src = DI_SYNC_CLK,307 .repeat_count = sig->mode.hactive,308 .cnt_clr_src = 5, /* Line Active */309 } , {310 /* unused */311 } , {312 /* unused */313 },314 };315 /* can't use #7 and #8 for line active and pixel active counters */316 struct di_sync_config cfg_vga[] = {317 {318 /* 1: INT_HSYNC */319 .run_count = h_total - 1,320 .run_src = DI_SYNC_CLK,321 } , {322 /* 2: VSYNC */323 .run_count = v_total - 1,324 .run_src = DI_SYNC_INT_HSYNC,325 } , {326 /* 3: Line Active */327 .run_src = DI_SYNC_INT_HSYNC,328 .offset_count = sig->mode.vsync_len +329 sig->mode.vback_porch,330 .offset_src = DI_SYNC_INT_HSYNC,331 .repeat_count = sig->mode.vactive,332 .cnt_clr_src = 3 /* VSYNC */,333 } , {334 /* PIN4: HSYNC for VGA via TVEv2 on TQ MBa53 */335 .run_count = h_total - 1,336 .run_src = DI_SYNC_CLK,337 .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */338 .offset_src = DI_SYNC_CLK,339 .cnt_polarity_gen_en = 1,340 .cnt_polarity_trigger_src = DI_SYNC_CLK,341 .cnt_down = sig->mode.hsync_len * 2,342 } , {343 /* 5: Pixel Active signal to DC */344 .run_src = DI_SYNC_CLK,345 .offset_count = sig->mode.hsync_len +346 sig->mode.hback_porch,347 .offset_src = DI_SYNC_CLK,348 .repeat_count = sig->mode.hactive,349 .cnt_clr_src = 4, /* Line Active */350 } , {351 /* PIN6: VSYNC for VGA via TVEv2 on TQ MBa53 */352 .run_count = v_total - 1,353 .run_src = DI_SYNC_INT_HSYNC,354 .offset_count = 1, /* magic value from Freescale TVE driver */355 .offset_src = DI_SYNC_INT_HSYNC,356 .cnt_polarity_gen_en = 1,357 .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC,358 .cnt_down = sig->mode.vsync_len * 2,359 } , {360 /* PIN4: HSYNC for VGA via TVEv2 on i.MX53-QSB */361 .run_count = h_total - 1,362 .run_src = DI_SYNC_CLK,363 .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */364 .offset_src = DI_SYNC_CLK,365 .cnt_polarity_gen_en = 1,366 .cnt_polarity_trigger_src = DI_SYNC_CLK,367 .cnt_down = sig->mode.hsync_len * 2,368 } , {369 /* PIN6: VSYNC for VGA via TVEv2 on i.MX53-QSB */370 .run_count = v_total - 1,371 .run_src = DI_SYNC_INT_HSYNC,372 .offset_count = 1, /* magic value from Freescale TVE driver */373 .offset_src = DI_SYNC_INT_HSYNC,374 .cnt_polarity_gen_en = 1,375 .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC,376 .cnt_down = sig->mode.vsync_len * 2,377 } , {378 /* unused */379 },380 };381 382 ipu_di_write(di, v_total - 1, DI_SCR_CONF);383 if (sig->hsync_pin == 2 && sig->vsync_pin == 3)384 ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg));385 else386 ipu_di_sync_config(di, cfg_vga, 0, ARRAY_SIZE(cfg_vga));387}388 389static void ipu_di_config_clock(struct ipu_di *di,390 const struct ipu_di_signal_cfg *sig)391{392 struct clk *clk;393 unsigned clkgen0;394 uint32_t val;395 396 if (sig->clkflags & IPU_DI_CLKMODE_EXT) {397 /*398 * CLKMODE_EXT means we must use the DI clock: this is399 * needed for things like LVDS which needs to feed the400 * DI and LDB with the same pixel clock.401 */402 clk = di->clk_di;403 404 if (sig->clkflags & IPU_DI_CLKMODE_SYNC) {405 /*406 * CLKMODE_SYNC means that we want the DI to be407 * clocked at the same rate as the parent clock.408 * This is needed (eg) for LDB which needs to be409 * fed with the same pixel clock. We assume that410 * the LDB clock has already been set correctly.411 */412 clkgen0 = 1 << 4;413 } else {414 /*415 * We can use the divider. We should really have416 * a flag here indicating whether the bridge can417 * cope with a fractional divider or not. For the418 * time being, let's go for simplicitly and419 * reliability.420 */421 unsigned long in_rate;422 unsigned div;423 424 clk_set_rate(clk, sig->mode.pixelclock);425 426 in_rate = clk_get_rate(clk);427 div = DIV_ROUND_CLOSEST(in_rate, sig->mode.pixelclock);428 div = clamp(div, 1U, 255U);429 430 clkgen0 = div << 4;431 }432 } else {433 /*434 * For other interfaces, we can arbitarily select between435 * the DI specific clock and the internal IPU clock. See436 * DI_GENERAL bit 20. We select the IPU clock if it can437 * give us a clock rate within 1% of the requested frequency,438 * otherwise we use the DI clock.439 */440 unsigned long rate, clkrate;441 unsigned div, error;442 443 clkrate = clk_get_rate(di->clk_ipu);444 div = DIV_ROUND_CLOSEST(clkrate, sig->mode.pixelclock);445 div = clamp(div, 1U, 255U);446 rate = clkrate / div;447 448 error = rate / (sig->mode.pixelclock / 1000);449 450 dev_dbg(di->ipu->dev, " IPU clock can give %lu with divider %u, error %c%d.%d%%\n",451 rate, div, error < 1000 ? '-' : '+',452 abs(error - 1000) / 10, abs(error - 1000) % 10);453 454 /* Allow a 1% error */455 if (error < 1010 && error >= 990) {456 clk = di->clk_ipu;457 458 clkgen0 = div << 4;459 } else {460 unsigned long in_rate;461 unsigned div;462 463 clk = di->clk_di;464 465 clk_set_rate(clk, sig->mode.pixelclock);466 467 in_rate = clk_get_rate(clk);468 div = DIV_ROUND_CLOSEST(in_rate, sig->mode.pixelclock);469 div = clamp(div, 1U, 255U);470 471 clkgen0 = div << 4;472 }473 }474 475 di->clk_di_pixel = clk;476 477 /* Set the divider */478 ipu_di_write(di, clkgen0, DI_BS_CLKGEN0);479 480 /*481 * Set the high/low periods. Bits 24:16 give us the falling edge,482 * and bits 8:0 give the rising edge. LSB is fraction, and is483 * based on the divider above. We want a 50% duty cycle, so set484 * the falling edge to be half the divider.485 */486 ipu_di_write(di, (clkgen0 >> 4) << 16, DI_BS_CLKGEN1);487 488 /* Finally select the input clock */489 val = ipu_di_read(di, DI_GENERAL) & ~DI_GEN_DI_CLK_EXT;490 if (clk == di->clk_di)491 val |= DI_GEN_DI_CLK_EXT;492 ipu_di_write(di, val, DI_GENERAL);493 494 dev_dbg(di->ipu->dev, "Want %luHz IPU %luHz DI %luHz using %s, %luHz\n",495 sig->mode.pixelclock,496 clk_get_rate(di->clk_ipu),497 clk_get_rate(di->clk_di),498 clk == di->clk_di ? "DI" : "IPU",499 clk_get_rate(di->clk_di_pixel) / (clkgen0 >> 4));500}501 502/*503 * This function is called to adjust a video mode to IPU restrictions.504 * It is meant to be called from drm crtc mode_fixup() methods.505 */506int ipu_di_adjust_videomode(struct ipu_di *di, struct videomode *mode)507{508 u32 diff;509 510 if (!IS_ALIGNED(mode->hactive, 8) &&511 mode->hfront_porch < ALIGN(mode->hactive, 8) - mode->hactive) {512 dev_err(di->ipu->dev, "hactive %d is not aligned to 8 and front porch is too small to compensate\n",513 mode->hactive);514 return -EINVAL;515 }516 517 if (mode->vfront_porch >= 2)518 return 0;519 520 diff = 2 - mode->vfront_porch;521 522 if (mode->vback_porch >= diff) {523 mode->vfront_porch = 2;524 mode->vback_porch -= diff;525 } else if (mode->vsync_len > diff) {526 mode->vfront_porch = 2;527 mode->vsync_len = mode->vsync_len - diff;528 } else {529 dev_warn(di->ipu->dev, "failed to adjust videomode\n");530 return -EINVAL;531 }532 533 dev_dbg(di->ipu->dev, "videomode adapted for IPU restrictions\n");534 return 0;535}536EXPORT_SYMBOL_GPL(ipu_di_adjust_videomode);537 538static u32 ipu_di_gen_polarity(int pin)539{540 switch (pin) {541 case 1:542 return DI_GEN_POLARITY_1;543 case 2:544 return DI_GEN_POLARITY_2;545 case 3:546 return DI_GEN_POLARITY_3;547 case 4:548 return DI_GEN_POLARITY_4;549 case 5:550 return DI_GEN_POLARITY_5;551 case 6:552 return DI_GEN_POLARITY_6;553 case 7:554 return DI_GEN_POLARITY_7;555 case 8:556 return DI_GEN_POLARITY_8;557 }558 return 0;559}560 561int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)562{563 u32 reg;564 u32 di_gen, vsync_cnt;565 u32 div;566 567 dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d\n",568 di->id, sig->mode.hactive, sig->mode.vactive);569 570 dev_dbg(di->ipu->dev, "Clocks: IPU %luHz DI %luHz Needed %luHz\n",571 clk_get_rate(di->clk_ipu),572 clk_get_rate(di->clk_di),573 sig->mode.pixelclock);574 575 mutex_lock(&di_mutex);576 577 ipu_di_config_clock(di, sig);578 579 div = ipu_di_read(di, DI_BS_CLKGEN0) & 0xfff;580 div = div / 16; /* Now divider is integer portion */581 582 /* Setup pixel clock timing */583 /* Down time is half of period */584 ipu_di_write(di, (div << 16), DI_BS_CLKGEN1);585 586 ipu_di_data_wave_config(di, SYNC_WAVE, div - 1, div - 1);587 ipu_di_data_pin_config(di, SYNC_WAVE, DI_PIN15, 3, 0, div * 2);588 589 di_gen = ipu_di_read(di, DI_GENERAL) & DI_GEN_DI_CLK_EXT;590 di_gen |= DI_GEN_DI_VSYNC_EXT;591 592 if (sig->mode.flags & DISPLAY_FLAGS_INTERLACED) {593 ipu_di_sync_config_interlaced(di, sig);594 595 /* set y_sel = 1 */596 di_gen |= 0x10000000;597 598 vsync_cnt = 3;599 } else {600 ipu_di_sync_config_noninterlaced(di, sig, div);601 602 vsync_cnt = 3;603 if (di->id == 1)604 /*605 * TODO: change only for TVEv2, parallel display606 * uses pin 2 / 3607 */608 if (!(sig->hsync_pin == 2 && sig->vsync_pin == 3))609 vsync_cnt = 6;610 }611 612 if (sig->mode.flags & DISPLAY_FLAGS_HSYNC_HIGH)613 di_gen |= ipu_di_gen_polarity(sig->hsync_pin);614 if (sig->mode.flags & DISPLAY_FLAGS_VSYNC_HIGH)615 di_gen |= ipu_di_gen_polarity(sig->vsync_pin);616 617 if (sig->clk_pol)618 di_gen |= DI_GEN_POLARITY_DISP_CLK;619 620 ipu_di_write(di, di_gen, DI_GENERAL);621 622 ipu_di_write(di, (--vsync_cnt << DI_VSYNC_SEL_OFFSET) | 0x00000002,623 DI_SYNC_AS_GEN);624 625 reg = ipu_di_read(di, DI_POL);626 reg &= ~(DI_POL_DRDY_DATA_POLARITY | DI_POL_DRDY_POLARITY_15);627 628 if (sig->enable_pol)629 reg |= DI_POL_DRDY_POLARITY_15;630 if (sig->data_pol)631 reg |= DI_POL_DRDY_DATA_POLARITY;632 633 ipu_di_write(di, reg, DI_POL);634 635 mutex_unlock(&di_mutex);636 637 return 0;638}639EXPORT_SYMBOL_GPL(ipu_di_init_sync_panel);640 641int ipu_di_enable(struct ipu_di *di)642{643 int ret;644 645 WARN_ON(IS_ERR(di->clk_di_pixel));646 647 ret = clk_prepare_enable(di->clk_di_pixel);648 if (ret)649 return ret;650 651 ipu_module_enable(di->ipu, di->module);652 653 return 0;654}655EXPORT_SYMBOL_GPL(ipu_di_enable);656 657int ipu_di_disable(struct ipu_di *di)658{659 WARN_ON(IS_ERR(di->clk_di_pixel));660 661 ipu_module_disable(di->ipu, di->module);662 663 clk_disable_unprepare(di->clk_di_pixel);664 665 return 0;666}667EXPORT_SYMBOL_GPL(ipu_di_disable);668 669int ipu_di_get_num(struct ipu_di *di)670{671 return di->id;672}673EXPORT_SYMBOL_GPL(ipu_di_get_num);674 675static DEFINE_MUTEX(ipu_di_lock);676 677struct ipu_di *ipu_di_get(struct ipu_soc *ipu, int disp)678{679 struct ipu_di *di;680 681 if (disp > 1)682 return ERR_PTR(-EINVAL);683 684 di = ipu->di_priv[disp];685 686 mutex_lock(&ipu_di_lock);687 688 if (di->inuse) {689 di = ERR_PTR(-EBUSY);690 goto out;691 }692 693 di->inuse = true;694out:695 mutex_unlock(&ipu_di_lock);696 697 return di;698}699EXPORT_SYMBOL_GPL(ipu_di_get);700 701void ipu_di_put(struct ipu_di *di)702{703 mutex_lock(&ipu_di_lock);704 705 di->inuse = false;706 707 mutex_unlock(&ipu_di_lock);708}709EXPORT_SYMBOL_GPL(ipu_di_put);710 711int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id,712 unsigned long base,713 u32 module, struct clk *clk_ipu)714{715 struct ipu_di *di;716 717 if (id > 1)718 return -ENODEV;719 720 di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL);721 if (!di)722 return -ENOMEM;723 724 ipu->di_priv[id] = di;725 726 di->clk_di = devm_clk_get(dev, id ? "di1" : "di0");727 if (IS_ERR(di->clk_di))728 return PTR_ERR(di->clk_di);729 730 di->module = module;731 di->id = id;732 di->clk_ipu = clk_ipu;733 di->base = devm_ioremap(dev, base, PAGE_SIZE);734 if (!di->base)735 return -ENOMEM;736 737 ipu_di_write(di, 0x10, DI_BS_CLKGEN0);738 739 dev_dbg(dev, "DI%d base: 0x%08lx remapped to %p\n",740 id, base, di->base);741 di->inuse = false;742 di->ipu = ipu;743 744 return 0;745}746 747void ipu_di_exit(struct ipu_soc *ipu, int id)748{749}750