uboot/drivers/video/seps525.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * FB driver for the WiseChip Semiconductor Inc. (UG-6028GDEBF02) display
   4 * using the SEPS525 (Syncoam) LCD Controller
   5 *
   6 * Copyright (C) 2020 Xilinx Inc.
   7 */
   8
   9#include <common.h>
  10#include <command.h>
  11#include <cpu_func.h>
  12#include <dm.h>
  13#include <errno.h>
  14#include <spi.h>
  15#include <video.h>
  16#include <asm/gpio.h>
  17#include <dm/device_compat.h>
  18#include <linux/delay.h>
  19
  20#define WIDTH           160
  21#define HEIGHT          128
  22
  23#define SEPS525_INDEX                   0x00
  24#define SEPS525_STATUS_RD               0x01
  25#define SEPS525_OSC_CTL                 0x02
  26#define SEPS525_IREF                    0x80
  27#define SEPS525_CLOCK_DIV               0x03
  28#define SEPS525_REDUCE_CURRENT          0x04
  29#define SEPS525_SOFT_RST                0x05
  30#define SEPS525_DISP_ONOFF              0x06
  31#define SEPS525_PRECHARGE_TIME_R        0x08
  32#define SEPS525_PRECHARGE_TIME_G        0x09
  33#define SEPS525_PRECHARGE_TIME_B        0x0A
  34#define SEPS525_PRECHARGE_CURRENT_R     0x0B
  35#define SEPS525_PRECHARGE_CURRENT_G     0x0C
  36#define SEPS525_PRECHARGE_CURRENT_B     0x0D
  37#define SEPS525_DRIVING_CURRENT_R       0x10
  38#define SEPS525_DRIVING_CURRENT_G       0x11
  39#define SEPS525_DRIVING_CURRENT_B       0x12
  40#define SEPS525_DISPLAYMODE_SET         0x13
  41#define SEPS525_RGBIF                   0x14
  42#define SEPS525_RGB_POL                 0x15
  43#define SEPS525_MEMORY_WRITEMODE        0x16
  44#define SEPS525_MX1_ADDR                0x17
  45#define SEPS525_MX2_ADDR                0x18
  46#define SEPS525_MY1_ADDR                0x19
  47#define SEPS525_MY2_ADDR                0x1A
  48#define SEPS525_MEMORY_ACCESS_POINTER_X 0x20
  49#define SEPS525_MEMORY_ACCESS_POINTER_Y 0x21
  50#define SEPS525_DDRAM_DATA_ACCESS_PORT  0x22
  51#define SEPS525_GRAY_SCALE_TABLE_INDEX  0x50
  52#define SEPS525_GRAY_SCALE_TABLE_DATA   0x51
  53#define SEPS525_DUTY                    0x28
  54#define SEPS525_DSL                     0x29
  55#define SEPS525_D1_DDRAM_FAC            0x2E
  56#define SEPS525_D1_DDRAM_FAR            0x2F
  57#define SEPS525_D2_DDRAM_SAC            0x31
  58#define SEPS525_D2_DDRAM_SAR            0x32
  59#define SEPS525_SCR1_FX1                0x33
  60#define SEPS525_SCR1_FX2                0x34
  61#define SEPS525_SCR1_FY1                0x35
  62#define SEPS525_SCR1_FY2                0x36
  63#define SEPS525_SCR2_SX1                0x37
  64#define SEPS525_SCR2_SX2                0x38
  65#define SEPS525_SCR2_SY1                0x39
  66#define SEPS525_SCR2_SY2                0x3A
  67#define SEPS525_SCREEN_SAVER_CONTEROL   0x3B
  68#define SEPS525_SS_SLEEP_TIMER          0x3C
  69#define SEPS525_SCREEN_SAVER_MODE       0x3D
  70#define SEPS525_SS_SCR1_FU              0x3E
  71#define SEPS525_SS_SCR1_MXY             0x3F
  72#define SEPS525_SS_SCR2_FU              0x40
  73#define SEPS525_SS_SCR2_MXY             0x41
  74#define SEPS525_MOVING_DIRECTION        0x42
  75#define SEPS525_SS_SCR2_SX1             0x47
  76#define SEPS525_SS_SCR2_SX2             0x48
  77#define SEPS525_SS_SCR2_SY1             0x49
  78#define SEPS525_SS_SCR2_SY2             0x4A
  79
  80/* SEPS525_DISPLAYMODE_SET */
  81#define MODE_SWAP_BGR   BIT(7)
  82#define MODE_SM         BIT(6)
  83#define MODE_RD         BIT(5)
  84#define MODE_CD         BIT(4)
  85
  86/**
  87 * struct seps525_priv - Private structure
  88 * @reset_gpio: Reset gpio pin
  89 * @dc_gpio: Data/command control gpio pin
  90 * @dev: Device uclass for video_ops
  91 */
  92struct seps525_priv {
  93        struct gpio_desc reset_gpio;
  94        struct gpio_desc dc_gpio;
  95        struct udevice *dev;
  96};
  97
  98static int seps525_spi_write_cmd(struct udevice *dev, u32 reg)
  99{
 100        struct seps525_priv *priv = dev_get_priv(dev);
 101        u8 buf8 = reg;
 102        int ret;
 103
 104        ret = dm_gpio_set_value(&priv->dc_gpio, 0);
 105        if (ret) {
 106                dev_dbg(dev, "Failed to handle dc\n");
 107                return ret;
 108        }
 109
 110        ret = dm_spi_xfer(dev, 8, &buf8, NULL, SPI_XFER_BEGIN | SPI_XFER_END);
 111        if (ret)
 112                dev_dbg(dev, "Failed to write command\n");
 113
 114        return ret;
 115}
 116
 117static int seps525_spi_write_data(struct udevice *dev, u32 val)
 118{
 119        struct seps525_priv *priv = dev_get_priv(dev);
 120        u8 buf8 = val;
 121        int ret;
 122
 123        ret = dm_gpio_set_value(&priv->dc_gpio, 1);
 124        if (ret) {
 125                dev_dbg(dev, "Failed to handle dc\n");
 126                return ret;
 127        }
 128
 129        ret = dm_spi_xfer(dev, 8, &buf8, NULL, SPI_XFER_BEGIN | SPI_XFER_END);
 130        if (ret)
 131                dev_dbg(dev, "Failed to write data\n");
 132
 133        return ret;
 134}
 135
 136static void seps525_spi_write(struct udevice *dev, u32 reg, u32 val)
 137{
 138        (void)seps525_spi_write_cmd(dev, reg);
 139        (void)seps525_spi_write_data(dev, val);
 140}
 141
 142static int seps525_display_init(struct udevice *dev)
 143{
 144        /* Disable Oscillator Power Down */
 145        seps525_spi_write(dev, SEPS525_REDUCE_CURRENT, 0x03);
 146        mdelay(5);
 147
 148        /* Set Normal Driving Current */
 149        seps525_spi_write(dev, SEPS525_REDUCE_CURRENT, 0x00);
 150        mdelay(5);
 151
 152        seps525_spi_write(dev, SEPS525_SCREEN_SAVER_CONTEROL, 0x00);
 153        /* Set EXPORT1 Pin at Internal Clock */
 154        seps525_spi_write(dev, SEPS525_OSC_CTL, 0x01);
 155        /* Set Clock as 120 Frames/Sec */
 156        seps525_spi_write(dev, SEPS525_CLOCK_DIV, 0x90);
 157        /* Set Reference Voltage Controlled by External Resister */
 158        seps525_spi_write(dev, SEPS525_IREF, 0x01);
 159
 160        /* precharge time R G B */
 161        seps525_spi_write(dev, SEPS525_PRECHARGE_TIME_R, 0x04);
 162        seps525_spi_write(dev, SEPS525_PRECHARGE_TIME_G, 0x05);
 163        seps525_spi_write(dev, SEPS525_PRECHARGE_TIME_B, 0x05);
 164
 165        /* precharge current R G B (uA) */
 166        seps525_spi_write(dev, SEPS525_PRECHARGE_CURRENT_R, 0x9D);
 167        seps525_spi_write(dev, SEPS525_PRECHARGE_CURRENT_G, 0x8C);
 168        seps525_spi_write(dev, SEPS525_PRECHARGE_CURRENT_B, 0x57);
 169
 170        /* driving current R G B (uA) */
 171        seps525_spi_write(dev, SEPS525_DRIVING_CURRENT_R, 0x56);
 172        seps525_spi_write(dev, SEPS525_DRIVING_CURRENT_G, 0x4D);
 173        seps525_spi_write(dev, SEPS525_DRIVING_CURRENT_B, 0x46);
 174        /* Set Color Sequence */
 175        seps525_spi_write(dev, SEPS525_DISPLAYMODE_SET, 0x00);
 176        /* Set MCU Interface Mode */
 177        seps525_spi_write(dev, SEPS525_RGBIF, 0x01);
 178        /* Set Memory Write Mode */
 179        seps525_spi_write(dev, SEPS525_MEMORY_WRITEMODE, 0x66);
 180        /* 1/128 Duty (0x0F~0x7F) */
 181        seps525_spi_write(dev, SEPS525_DUTY, 0x7F);
 182        /* Set Mapping RAM Display Start Line (0x00~0x7F) */
 183        seps525_spi_write(dev, SEPS525_DSL, 0x00);
 184        /* Display On (0x00/0x01) */
 185        seps525_spi_write(dev, SEPS525_DISP_ONOFF, 0x01);
 186        /* Set All Internal Register Value as Normal Mode */
 187        seps525_spi_write(dev, SEPS525_SOFT_RST, 0x00);
 188        /* Set RGB Interface Polarity as Active Low */
 189        seps525_spi_write(dev, SEPS525_RGB_POL, 0x00);
 190
 191        /* Enable access for data */
 192        (void)seps525_spi_write_cmd(dev, SEPS525_DDRAM_DATA_ACCESS_PORT);
 193
 194        return 0;
 195}
 196
 197static int seps525_spi_startup(struct udevice *dev)
 198{
 199        struct seps525_priv *priv = dev_get_priv(dev);
 200        int ret;
 201
 202        ret = dm_gpio_set_value(&priv->reset_gpio, 1);
 203        if (ret)
 204                return ret;
 205
 206        ret = dm_gpio_set_value(&priv->reset_gpio, 0);
 207        if (ret)
 208                return ret;
 209
 210        ret = dm_spi_claim_bus(dev);
 211        if (ret) {
 212                dev_err(dev, "Failed to claim SPI bus: %d\n", ret);
 213                return ret;
 214        }
 215
 216        ret = seps525_display_init(dev);
 217        if (ret)
 218                return ret;
 219
 220        dm_spi_release_bus(dev);
 221
 222        return 0;
 223}
 224
 225static int seps525_sync(struct udevice *vid)
 226{
 227        struct video_priv *uc_priv = dev_get_uclass_priv(vid);
 228        struct seps525_priv *priv = dev_get_priv(vid);
 229        struct udevice *dev = priv->dev;
 230        int i, ret;
 231        u8 data1, data2;
 232        u8 *start = uc_priv->fb;
 233
 234        ret = dm_spi_claim_bus(dev);
 235        if (ret) {
 236                dev_err(dev, "Failed to claim SPI bus: %d\n", ret);
 237                return ret;
 238        }
 239
 240        /* start position X,Y */
 241        seps525_spi_write(dev, SEPS525_MEMORY_ACCESS_POINTER_X, 0);
 242        seps525_spi_write(dev, SEPS525_MEMORY_ACCESS_POINTER_Y, 0);
 243
 244        /* Enable access for data */
 245        (void)seps525_spi_write_cmd(dev, SEPS525_DDRAM_DATA_ACCESS_PORT);
 246
 247        for (i = 0; i < (uc_priv->xsize * uc_priv->ysize); i++) {
 248                data2 = *start++;
 249                data1 = *start++;
 250                (void)seps525_spi_write_data(dev, data1);
 251                (void)seps525_spi_write_data(dev, data2);
 252        }
 253
 254        dm_spi_release_bus(dev);
 255
 256        return 0;
 257}
 258
 259static int seps525_probe(struct udevice *dev)
 260{
 261        struct video_priv *uc_priv = dev_get_uclass_priv(dev);
 262        struct seps525_priv *priv = dev_get_priv(dev);
 263        u32 buswidth;
 264        int ret;
 265
 266        buswidth = dev_read_u32_default(dev, "buswidth", 0);
 267        if (buswidth != 8) {
 268                dev_err(dev, "Only 8bit buswidth is supported now");
 269                return -EINVAL;
 270        }
 271
 272        ret = gpio_request_by_name(dev, "reset-gpios", 0,
 273                                   &priv->reset_gpio, GPIOD_IS_OUT);
 274        if (ret) {
 275                dev_err(dev, "missing reset GPIO\n");
 276                return ret;
 277        }
 278
 279        ret = gpio_request_by_name(dev, "dc-gpios", 0,
 280                                   &priv->dc_gpio, GPIOD_IS_OUT);
 281        if (ret) {
 282                dev_err(dev, "missing dc GPIO\n");
 283                return ret;
 284        }
 285
 286        uc_priv->bpix = VIDEO_BPP16;
 287        uc_priv->xsize = WIDTH;
 288        uc_priv->ysize = HEIGHT;
 289        uc_priv->rot = 0;
 290
 291        priv->dev = dev;
 292
 293        ret = seps525_spi_startup(dev);
 294        if (ret)
 295                return ret;
 296
 297        return 0;
 298}
 299
 300static int seps525_bind(struct udevice *dev)
 301{
 302        struct video_uc_plat *plat = dev_get_uclass_plat(dev);
 303
 304        plat->size = WIDTH * HEIGHT * 16;
 305
 306        return 0;
 307}
 308
 309static const struct video_ops seps525_ops = {
 310        .video_sync = seps525_sync,
 311};
 312
 313static const struct udevice_id seps525_ids[] = {
 314        { .compatible = "syncoam,seps525" },
 315        { }
 316};
 317
 318U_BOOT_DRIVER(seps525_video) = {
 319        .name = "seps525_video",
 320        .id = UCLASS_VIDEO,
 321        .of_match = seps525_ids,
 322        .ops = &seps525_ops,
 323        .plat_auto = sizeof(struct video_uc_plat),
 324        .bind = seps525_bind,
 325        .probe = seps525_probe,
 326        .priv_auto = sizeof(struct seps525_priv),
 327};
 328