Skip to content

Commit da21a62

Browse files
committed
Add mode selection to iMX6 IPU driver
- Configure ipu1_di0 tob e sourced from the VIDEO_PLL(PLL5) and hardcode frequency to (455000000/3)Mhz. This value, further divided, can yield frequencies close enough to support 1080p, 720p, 1024x768, and 640x480 modes. This is not ideal but it's an improvement comparing to the only hardcoded 1024x768 mode. - Fix memory leaks if attach method failed - Print EDID when -v passed to the kernel
1 parent cbc596d commit da21a62

File tree

4 files changed

+182
-40
lines changed

4 files changed

+182
-40
lines changed

sys/arm/freescale/imx/imx6_ccm.c

Lines changed: 65 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,53 @@ imx_ccm_ahb_hz(void)
393393
return (132000000);
394394
}
395395

396+
int
397+
imx_ccm_pll_video_enable(void)
398+
{
399+
uint32_t reg;
400+
int timeout;
401+
402+
/* Power down PLL */
403+
reg = RD4(ccm_sc, CCM_ANALOG_PLL_VIDEO);
404+
reg &= ~CCM_ANALOG_PLL_VIDEO_POWERDOWN;
405+
WR4(ccm_sc, CCM_ANALOG_PLL_VIDEO, reg);
406+
407+
/*
408+
* Fvideo = Fref * (37 + 11/12) / 2
409+
* Fref = 24MHz, Fvideo = 455MHz
410+
*/
411+
reg &= ~CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT_MASK;
412+
reg |= CCM_ANALOG_PLL_VIDEO_POST_DIV_2;
413+
reg &= ~CCM_ANALOG_PLL_VIDEO_DIV_SELECT_MASK;
414+
reg |= 37 << CCM_ANALOG_PLL_VIDEO_DIV_SELECT_SHIFT;
415+
WR4(ccm_sc, CCM_ANALOG_PLL_VIDEO, reg);
416+
417+
WR4(ccm_sc, CCM_ANALOG_PLL_VIDEO_NUM, 11);
418+
WR4(ccm_sc, CCM_ANALOG_PLL_VIDEO_DENOM, 12);
419+
420+
/* Power up and wait for PLL lock down */
421+
reg = RD4(ccm_sc, CCM_ANALOG_PLL_VIDEO);
422+
reg &= ~CCM_ANALOG_PLL_VIDEO_POWERDOWN;
423+
WR4(ccm_sc, CCM_ANALOG_PLL_VIDEO, reg);
424+
425+
for (timeout = 100000; timeout > 0; timeout--) {
426+
if (RD4(ccm_sc, CCM_ANALOG_PLL_VIDEO) &
427+
CCM_ANALOG_PLL_VIDEO_LOCK) {
428+
break;
429+
}
430+
}
431+
if (timeout <= 0) {
432+
return ETIMEDOUT;
433+
}
434+
435+
/* Enable the PLL */
436+
reg |= CCM_ANALOG_PLL_VIDEO_ENABLE;
437+
reg &= ~CCM_ANALOG_PLL_VIDEO_BYPASS;
438+
WR4(ccm_sc, CCM_ANALOG_PLL_VIDEO, reg);
439+
440+
return (0);
441+
}
442+
396443
void
397444
imx_ccm_ipu_enable(int ipu)
398445
{
@@ -406,6 +453,24 @@ imx_ccm_ipu_enable(int ipu)
406453
else
407454
reg |= CCGR3_IPU2_IPU | CCGR3_IPU2_DI0;
408455
WR4(sc, CCM_CCGR3, reg);
456+
457+
/* Set IPU1_DI0 clock to source from PLL5 and divide it by 3 */
458+
reg = RD4(sc, CCM_CHSCCDR);
459+
reg &= ~(CHSCCDR_IPU1_DI0_PRE_CLK_SEL_MASK |
460+
CHSCCDR_IPU1_DI0_PODF_MASK | CHSCCDR_IPU1_DI0_CLK_SEL_MASK);
461+
reg |= (CHSCCDR_PODF_DIVIDE_BY_3 << CHSCCDR_IPU1_DI0_PODF_SHIFT);
462+
reg |= (CHSCCDR_IPU_PRE_CLK_PLL5 << CHSCCDR_IPU1_DI0_PRE_CLK_SEL_SHIFT);
463+
WR4(sc, CCM_CHSCCDR, reg);
464+
465+
reg |= (CHSCCDR_CLK_SEL_PREMUXED << CHSCCDR_IPU1_DI0_CLK_SEL_SHIFT);
466+
WR4(sc, CCM_CHSCCDR, reg);
467+
}
468+
469+
uint32_t
470+
imx_ccm_ipu_hz(void)
471+
{
472+
473+
return (455000000 / 3);
409474
}
410475

411476
void
@@ -418,16 +483,6 @@ imx_ccm_hdmi_enable(void)
418483
reg = RD4(sc, CCM_CCGR2);
419484
reg |= CCGR2_HDMI_TX | CCGR2_HDMI_TX_ISFR;
420485
WR4(sc, CCM_CCGR2, reg);
421-
422-
/* Set HDMI clock to 280MHz */
423-
reg = RD4(sc, CCM_CHSCCDR);
424-
reg &= ~(CHSCCDR_IPU1_DI0_PRE_CLK_SEL_MASK |
425-
CHSCCDR_IPU1_DI0_PODF_MASK | CHSCCDR_IPU1_DI0_CLK_SEL_MASK);
426-
reg |= (CHSCCDR_PODF_DIVIDE_BY_3 << CHSCCDR_IPU1_DI0_PODF_SHIFT);
427-
reg |= (CHSCCDR_IPU_PRE_CLK_540M_PFD << CHSCCDR_IPU1_DI0_PRE_CLK_SEL_SHIFT);
428-
WR4(sc, CCM_CHSCCDR, reg);
429-
reg |= (CHSCCDR_CLK_SEL_LDB_DI0 << CHSCCDR_IPU1_DI0_CLK_SEL_SHIFT);
430-
WR4(sc, CCM_CHSCCDR, reg);
431486
}
432487

433488
uint32_t

sys/arm/freescale/imx/imx6_ccmreg.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,12 @@
6464
#define CHSCCDR_IPU1_DI0_PODF_SHIFT 3
6565
#define CHSCCDR_IPU1_DI0_CLK_SEL_MASK (0x7)
6666
#define CHSCCDR_IPU1_DI0_CLK_SEL_SHIFT 0
67+
#define CHSCCDR_CLK_SEL_PREMUXED 0
6768
#define CHSCCDR_CLK_SEL_LDB_DI0 3
6869
#define CHSCCDR_PODF_DIVIDE_BY_3 2
70+
#define CHSCCDR_PODF_DIVIDE_BY_1 0
6971
#define CHSCCDR_IPU_PRE_CLK_540M_PFD 5
72+
#define CHSCCDR_IPU_PRE_CLK_PLL5 2
7073
#define CCM_CSCDR2 0x038
7174
#define CCM_CLPCR 0x054
7275
#define CCM_CLPCR_LPM_MASK 0x03
@@ -138,6 +141,19 @@
138141
#define CCGR6_USDHC3 (0x3 << 6)
139142
#define CCGR6_USDHC4 (0x3 << 8)
140143
#define CCM_CMEOR 0x088
144+
145+
#define CCM_ANALOG_PLL_VIDEO 0x000040a0
146+
#define CCM_ANALOG_PLL_VIDEO_LOCK (1u << 31)
147+
#define CCM_ANALOG_PLL_VIDEO_BYPASS (1u << 16)
148+
#define CCM_ANALOG_PLL_VIDEO_ENABLE (1u << 13)
149+
#define CCM_ANALOG_PLL_VIDEO_POWERDOWN (1u << 12)
150+
#define CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT_MASK (3u << 19)
151+
#define CCM_ANALOG_PLL_VIDEO_POST_DIV_2 (1u << 19)
152+
#define CCM_ANALOG_PLL_VIDEO_DIV_SELECT_MASK (0x7f << 0)
153+
#define CCM_ANALOG_PLL_VIDEO_DIV_SELECT_SHIFT 0
154+
155+
#define CCM_ANALOG_PLL_VIDEO_NUM 0x000040b0
156+
#define CCM_ANALOG_PLL_VIDEO_DENOM 0x000040c0
141157

142158
#define CCM_ANALOG_PLL_ENET 0x000040e0
143159
#define CCM_ANALOG_PLL_ENET_LOCK (1u << 31)

sys/arm/freescale/imx/imx6_ipu.c

Lines changed: 99 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,8 @@ __FBSDID("$FreeBSD$");
6161
#include "fb_if.h"
6262
#include "hdmi_if.h"
6363

64-
#define EDID_DEBUG_not
65-
6664
static int have_ipu = 0;
6765

68-
#define LDB_CLOCK_RATE 280000000
69-
7066
#define MODE_HBP(mode) ((mode)->htotal - (mode)->hsync_end)
7167
#define MODE_HFP(mode) ((mode)->hsync_start - (mode)->hdisplay)
7268
#define MODE_HSW(mode) ((mode)->hsync_end - (mode)->hsync_start)
@@ -77,11 +73,6 @@ static int have_ipu = 0;
7773
#define MODE_BPP 16
7874
#define MODE_PIXEL_CLOCK_INVERT 1
7975

80-
#define M(nm,hr,vr,clk,hs,he,ht,vs,ve,vt,f) \
81-
{ clk, hr, hs, he, ht, vr, vs, ve, vt, f, nm }
82-
83-
static struct videomode mode1024x768 = M("1024x768x60",1024,768,65000,1048,1184,1344,771,777,806,VID_NHSYNC|VID_PHSYNC);
84-
8576
#define DMA_CHANNEL 23
8677
#define DC_CHAN5 5
8778
#define DI_PORT 0
@@ -384,7 +375,7 @@ struct ipu_softc {
384375
void *sc_intr_hl;
385376
struct mtx sc_mtx;
386377
struct fb_info sc_fb_info;
387-
struct videomode *sc_mode;
378+
const struct videomode *sc_mode;
388379

389380
/* Framebuffer */
390381
bus_dma_tag_t sc_dma_tag;
@@ -634,36 +625,49 @@ ipu_init_microcode_template(struct ipu_softc *sc, int di, int map)
634625
}
635626
}
636627

628+
static uint32_t
629+
ipu_calc_divisor(uint32_t reference, uint32_t freq)
630+
{
631+
uint32_t div, i;
632+
uint32_t delta, min_delta;
633+
634+
min_delta = freq;
635+
div = 255;
636+
637+
for (i = 1; i < 255; i++) {
638+
delta = abs(reference/i - freq);
639+
if (delta < min_delta) {
640+
div = i;
641+
min_delta = delta;
642+
}
643+
}
644+
645+
return (div);
646+
}
647+
637648
static void
638649
ipu_config_timing(struct ipu_softc *sc, int di)
639650
{
640-
int div;
651+
uint32_t div;
641652
uint32_t di_scr_conf;
642653
uint32_t gen_offset, gen;
643654
uint32_t as_gen_offset, as_gen;
644655
uint32_t dw_gen_offset, dw_gen;
645656
uint32_t dw_set_offset, dw_set;
646657
uint32_t bs_clkgen_offset;
647658
int map;
659+
uint32_t freq;
660+
661+
freq = sc->sc_mode->dot_clock * 1000;
648662

649-
/* TODO: check mode restrictions / fixup */
650-
/* TODO: enable timers, get divisors */
651-
div = 1;
663+
div = ipu_calc_divisor(imx_ccm_ipu_hz(), freq);
652664
map = 0;
653665

654666
bs_clkgen_offset = di ? IPU_DI1_BS_CLKGEN0 : IPU_DI0_BS_CLKGEN0;
655667
IPU_WRITE4(sc, bs_clkgen_offset, DI_BS_CLKGEN0(div, 0));
656668
/* half of the divider */
657669
IPU_WRITE4(sc, bs_clkgen_offset + 4, DI_BS_CLKGEN1_DOWN(div / 2, div % 2));
658670

659-
/*
660-
* TODO: Configure LLDB clock by changing following fields
661-
* in CCM fields:
662-
* CS2CDR_LDB_DI0_CLK_SEL
663-
* CSCMR2_LDB_DI0_IPU_DIV
664-
* CBCDR_MMDC_CH1_AXI_PODF
665-
*/
666-
667671
/* Setup wave generator */
668672
dw_gen_offset = di ? IPU_DI1_DW_GEN_0 : IPU_DI0_DW_GEN_0;
669673
dw_gen = DW_GEN_DI_ACCESS_SIZE(div - 1) | DW_GEN_DI_COMPONENT_SIZE(div - 1);
@@ -768,8 +772,6 @@ ipu_dc_enable(struct ipu_softc *sc)
768772
conf &= ~WRITE_CH_CONF_PROG_CHAN_TYP_MASK;
769773
conf |= WRITE_CH_CONF_PROG_CHAN_NORMAL;
770774
IPU_WRITE4(sc, DC_WRITE_CH_CONF_5, conf);
771-
772-
/* TODO: enable clock */
773775
}
774776

775777
static void
@@ -1063,15 +1065,55 @@ ipu_init(struct ipu_softc *sc)
10631065
return (err);
10641066
}
10651067

1068+
static int
1069+
ipu_mode_is_valid(const struct videomode *mode)
1070+
{
1071+
if ((mode->dot_clock < 13500) || (mode->dot_clock > 216000))
1072+
return (0);
1073+
1074+
return (1);
1075+
}
1076+
1077+
static const struct videomode *
1078+
ipu_pick_mode(struct edid_info *ei)
1079+
{
1080+
const struct videomode *videomode;
1081+
const struct videomode *m;
1082+
int n;
1083+
1084+
videomode = NULL;
1085+
1086+
/*
1087+
* Pick a mode.
1088+
*/
1089+
if (ei->edid_preferred_mode != NULL) {
1090+
if (ipu_mode_is_valid(ei->edid_preferred_mode))
1091+
videomode = ei->edid_preferred_mode;
1092+
}
1093+
1094+
if (videomode == NULL) {
1095+
m = ei->edid_modes;
1096+
1097+
sort_modes(ei->edid_modes,
1098+
&ei->edid_preferred_mode,
1099+
ei->edid_nmodes);
1100+
for (n = 0; n < ei->edid_nmodes; n++)
1101+
if (ipu_mode_is_valid(&m[n])) {
1102+
videomode = &m[n];
1103+
break;
1104+
}
1105+
}
1106+
1107+
return videomode;
1108+
}
1109+
10661110
static void
10671111
ipu_hdmi_event(void *arg, device_t hdmi_dev)
10681112
{
10691113
struct ipu_softc *sc;
10701114
uint8_t *edid;
10711115
uint32_t edid_len;
1072-
#ifdef EDID_DEBUG
10731116
struct edid_info ei;
1074-
#endif
10751117
const struct videomode *videomode;
10761118

10771119
sc = arg;
@@ -1084,14 +1126,28 @@ ipu_hdmi_event(void *arg, device_t hdmi_dev)
10841126

10851127
videomode = NULL;
10861128

1087-
#ifdef EDID_DEBUG
10881129
if ( edid && (edid_parse(edid, &ei) == 0)) {
1089-
edid_print(&ei);
1130+
if (bootverbose)
1131+
edid_print(&ei);
1132+
videomode = ipu_pick_mode(&ei);
10901133
} else
10911134
device_printf(sc->sc_dev, "failed to parse EDID\n");
1092-
#endif
10931135

1094-
sc->sc_mode = &mode1024x768;
1136+
/* Use standard VGA as fallback */
1137+
if (videomode == NULL)
1138+
videomode = pick_mode_by_ref(640, 480, 60);
1139+
1140+
if (videomode == NULL) {
1141+
device_printf(sc->sc_dev, "failed to find usable videomode\n");
1142+
return;
1143+
}
1144+
1145+
sc->sc_mode = videomode;
1146+
1147+
if (bootverbose)
1148+
device_printf(sc->sc_dev, "detected videomode: %dx%d\n",
1149+
videomode->hdisplay, videomode->vdisplay);
1150+
10951151
ipu_init(sc);
10961152

10971153
HDMI_SET_VIDEOMODE(hdmi_dev, sc->sc_mode);
@@ -1145,9 +1201,22 @@ ipu_attach(device_t dev)
11451201
}
11461202

11471203
/* Enable IPU1 */
1204+
if (imx_ccm_pll_video_enable() != 0) {
1205+
bus_release_resource(dev, SYS_RES_MEMORY,
1206+
sc->sc_mem_rid, sc->sc_mem_res);
1207+
bus_release_resource(dev, SYS_RES_IRQ,
1208+
sc->sc_irq_rid, sc->sc_irq_res);
1209+
device_printf(dev, "failed to set up video PLL\n");
1210+
return (ENXIO);
1211+
}
1212+
11481213
imx_ccm_ipu_enable(1);
11491214

11501215
if (src_reset_ipu() != 0) {
1216+
bus_release_resource(dev, SYS_RES_MEMORY,
1217+
sc->sc_mem_rid, sc->sc_mem_res);
1218+
bus_release_resource(dev, SYS_RES_IRQ,
1219+
sc->sc_irq_rid, sc->sc_irq_res);
11511220
device_printf(dev, "failed to reset IPU\n");
11521221
return (ENXIO);
11531222
}

sys/arm/freescale/imx/imx_ccmvar.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,12 @@ uint32_t imx_ccm_perclk_hz(void);
4949
uint32_t imx_ccm_sdhci_hz(void);
5050
uint32_t imx_ccm_uart_hz(void);
5151
uint32_t imx_ccm_ahb_hz(void);
52+
uint32_t imx_ccm_ipu_hz(void);
5253

5354
void imx_ccm_usb_enable(device_t _usbdev);
5455
void imx_ccm_usbphy_enable(device_t _phydev);
5556
void imx_ccm_ssi_configure(device_t _ssidev);
57+
int imx_ccm_pll_video_enable(void);
5658
void imx_ccm_hdmi_enable(void);
5759
void imx_ccm_ipu_enable(int ipu);
5860
int imx6_ccm_sata_enable(void);

0 commit comments

Comments
 (0)