uboot/drivers/video/atmel_lcdfb.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Driver for AT91/AT32 LCD Controller
   4 *
   5 * Copyright (C) 2007 Atmel Corporation
   6 */
   7
   8#include <common.h>
   9#include <atmel_lcd.h>
  10#include <dm.h>
  11#include <fdtdec.h>
  12#include <log.h>
  13#include <part.h>
  14#include <video.h>
  15#include <asm/global_data.h>
  16#include <asm/io.h>
  17#include <asm/arch/gpio.h>
  18#include <asm/arch/clk.h>
  19#include <bmp_layout.h>
  20#include <atmel_lcdc.h>
  21#include <linux/delay.h>
  22
  23DECLARE_GLOBAL_DATA_PTR;
  24
  25enum {
  26        /* Maximum LCD size we support */
  27        LCD_MAX_WIDTH           = 1366,
  28        LCD_MAX_HEIGHT          = 768,
  29        LCD_MAX_LOG2_BPP        = VIDEO_BPP16,
  30};
  31
  32struct atmel_fb_priv {
  33        struct display_timing timing;
  34};
  35
  36/* configurable parameters */
  37#define ATMEL_LCDC_CVAL_DEFAULT         0xc8
  38#define ATMEL_LCDC_DMA_BURST_LEN        8
  39#ifndef ATMEL_LCDC_GUARD_TIME
  40#define ATMEL_LCDC_GUARD_TIME           1
  41#endif
  42
  43#if defined(CONFIG_AT91SAM9263)
  44#define ATMEL_LCDC_FIFO_SIZE            2048
  45#else
  46#define ATMEL_LCDC_FIFO_SIZE            512
  47#endif
  48
  49#define lcdc_readl(mmio, reg)           __raw_readl((mmio)+(reg))
  50#define lcdc_writel(mmio, reg, val)     __raw_writel((val), (mmio)+(reg))
  51
  52static void atmel_fb_init(ulong addr, struct display_timing *timing, int bpix,
  53                          bool tft, bool cont_pol_low, ulong lcdbase)
  54{
  55        unsigned long value;
  56        void *reg = (void *)addr;
  57
  58        /* Turn off the LCD controller and the DMA controller */
  59        lcdc_writel(reg, ATMEL_LCDC_PWRCON,
  60                    ATMEL_LCDC_GUARD_TIME << ATMEL_LCDC_GUARDT_OFFSET);
  61
  62        /* Wait for the LCDC core to become idle */
  63        while (lcdc_readl(reg, ATMEL_LCDC_PWRCON) & ATMEL_LCDC_BUSY)
  64                udelay(10);
  65
  66        lcdc_writel(reg, ATMEL_LCDC_DMACON, 0);
  67
  68        /* Reset LCDC DMA */
  69        lcdc_writel(reg, ATMEL_LCDC_DMACON, ATMEL_LCDC_DMARST);
  70
  71        /* ...set frame size and burst length = 8 words (?) */
  72        value = (timing->hactive.typ * timing->vactive.typ *
  73                 (1 << bpix)) / 32;
  74        value |= ((ATMEL_LCDC_DMA_BURST_LEN - 1) << ATMEL_LCDC_BLENGTH_OFFSET);
  75        lcdc_writel(reg, ATMEL_LCDC_DMAFRMCFG, value);
  76
  77        /* Set pixel clock */
  78        value = get_lcdc_clk_rate(0) / timing->pixelclock.typ;
  79        if (get_lcdc_clk_rate(0) % timing->pixelclock.typ)
  80                value++;
  81        value = (value / 2) - 1;
  82
  83        if (!value) {
  84                lcdc_writel(reg, ATMEL_LCDC_LCDCON1, ATMEL_LCDC_BYPASS);
  85        } else
  86                lcdc_writel(reg, ATMEL_LCDC_LCDCON1,
  87                            value << ATMEL_LCDC_CLKVAL_OFFSET);
  88
  89        /* Initialize control register 2 */
  90        value = ATMEL_LCDC_MEMOR_LITTLE | ATMEL_LCDC_CLKMOD_ALWAYSACTIVE;
  91        if (tft)
  92                value |= ATMEL_LCDC_DISTYPE_TFT;
  93
  94        if (!(timing->flags & DISPLAY_FLAGS_HSYNC_HIGH))
  95                value |= ATMEL_LCDC_INVLINE_INVERTED;
  96        if (!(timing->flags & DISPLAY_FLAGS_VSYNC_HIGH))
  97                value |= ATMEL_LCDC_INVFRAME_INVERTED;
  98        value |= bpix << 5;
  99        lcdc_writel(reg, ATMEL_LCDC_LCDCON2, value);
 100
 101        /* Vertical timing */
 102        value = (timing->vsync_len.typ - 1) << ATMEL_LCDC_VPW_OFFSET;
 103        value |= timing->vback_porch.typ << ATMEL_LCDC_VBP_OFFSET;
 104        value |= timing->vfront_porch.typ;
 105        /* Magic! (Datasheet says "Bit 31 must be written to 1") */
 106        value |= 1U << 31;
 107        lcdc_writel(reg, ATMEL_LCDC_TIM1, value);
 108
 109        /* Horizontal timing */
 110        value = (timing->hfront_porch.typ - 1) << ATMEL_LCDC_HFP_OFFSET;
 111        value |= (timing->hsync_len.typ - 1) << ATMEL_LCDC_HPW_OFFSET;
 112        value |= (timing->hback_porch.typ - 1);
 113        lcdc_writel(reg, ATMEL_LCDC_TIM2, value);
 114
 115        /* Display size */
 116        value = (timing->hactive.typ - 1) << ATMEL_LCDC_HOZVAL_OFFSET;
 117        value |= timing->vactive.typ - 1;
 118        lcdc_writel(reg, ATMEL_LCDC_LCDFRMCFG, value);
 119
 120        /* FIFO Threshold: Use formula from data sheet */
 121        value = ATMEL_LCDC_FIFO_SIZE - (2 * ATMEL_LCDC_DMA_BURST_LEN + 3);
 122        lcdc_writel(reg, ATMEL_LCDC_FIFO, value);
 123
 124        /* Toggle LCD_MODE every frame */
 125        lcdc_writel(reg, ATMEL_LCDC_MVAL, 0);
 126
 127        /* Disable all interrupts */
 128        lcdc_writel(reg, ATMEL_LCDC_IDR, ~0UL);
 129
 130        /* Set contrast */
 131        value = ATMEL_LCDC_PS_DIV8 |
 132                ATMEL_LCDC_ENA_PWMENABLE;
 133        if (!cont_pol_low)
 134                value |= ATMEL_LCDC_POL_POSITIVE;
 135        lcdc_writel(reg, ATMEL_LCDC_CONTRAST_CTR, value);
 136        lcdc_writel(reg, ATMEL_LCDC_CONTRAST_VAL, ATMEL_LCDC_CVAL_DEFAULT);
 137
 138        /* Set framebuffer DMA base address and pixel offset */
 139        lcdc_writel(reg, ATMEL_LCDC_DMABADDR1, lcdbase);
 140
 141        lcdc_writel(reg, ATMEL_LCDC_DMACON, ATMEL_LCDC_DMAEN);
 142        lcdc_writel(reg, ATMEL_LCDC_PWRCON,
 143                    (ATMEL_LCDC_GUARD_TIME << ATMEL_LCDC_GUARDT_OFFSET) | ATMEL_LCDC_PWR);
 144}
 145
 146static int atmel_fb_lcd_probe(struct udevice *dev)
 147{
 148        struct video_uc_plat *uc_plat = dev_get_uclass_plat(dev);
 149        struct video_priv *uc_priv = dev_get_uclass_priv(dev);
 150        struct atmel_fb_priv *priv = dev_get_priv(dev);
 151        struct display_timing *timing = &priv->timing;
 152
 153        /*
 154         * For now some values are hard-coded. We could use the device tree
 155         * bindings in simple-framebuffer.txt to specify the format/bpp and
 156         * some Atmel-specific binding for tft and cont_pol_low.
 157         */
 158        atmel_fb_init(ATMEL_BASE_LCDC, timing, VIDEO_BPP16, true, false,
 159                      uc_plat->base);
 160        uc_priv->xsize = timing->hactive.typ;
 161        uc_priv->ysize = timing->vactive.typ;
 162        uc_priv->bpix = VIDEO_BPP16;
 163        video_set_flush_dcache(dev, true);
 164        debug("LCD frame buffer at %lx, size %x, %dx%d pixels\n", uc_plat->base,
 165              uc_plat->size, uc_priv->xsize, uc_priv->ysize);
 166
 167        return 0;
 168}
 169
 170static int atmel_fb_of_to_plat(struct udevice *dev)
 171{
 172        struct atmel_lcd_plat *plat = dev_get_plat(dev);
 173        struct atmel_fb_priv *priv = dev_get_priv(dev);
 174        struct display_timing *timing = &priv->timing;
 175        const void *blob = gd->fdt_blob;
 176
 177        if (fdtdec_decode_display_timing(blob, dev_of_offset(dev),
 178                                         plat->timing_index, timing)) {
 179                debug("%s: Failed to decode display timing\n", __func__);
 180                return -EINVAL;
 181        }
 182
 183        return 0;
 184}
 185
 186static int atmel_fb_lcd_bind(struct udevice *dev)
 187{
 188        struct video_uc_plat *uc_plat = dev_get_uclass_plat(dev);
 189
 190        uc_plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT *
 191                        (1 << VIDEO_BPP16) / 8;
 192        debug("%s: Frame buffer size %x\n", __func__, uc_plat->size);
 193
 194        return 0;
 195}
 196
 197static const struct udevice_id atmel_fb_lcd_ids[] = {
 198        { .compatible = "atmel,at91sam9g45-lcdc" },
 199        { }
 200};
 201
 202U_BOOT_DRIVER(atmel_fb) = {
 203        .name   = "atmel_fb",
 204        .id     = UCLASS_VIDEO,
 205        .of_match = atmel_fb_lcd_ids,
 206        .bind   = atmel_fb_lcd_bind,
 207        .of_to_plat     = atmel_fb_of_to_plat,
 208        .probe  = atmel_fb_lcd_probe,
 209        .plat_auto      = sizeof(struct atmel_lcd_plat),
 210        .priv_auto      = sizeof(struct atmel_fb_priv),
 211};
 212