linux/drivers/staging/fbtft/fb_ili9163.c
<<
>>
Prefs
   1/*
   2 * FB driver for the ILI9163 LCD Controller
   3 *
   4 * Copyright (C) 2015 Kozhevnikov Anatoly
   5 *
   6 * Based on ili9325.c by Noralf Tronnes and
   7 * .S.U.M.O.T.O.Y. by Max MC Costa (https://github.com/sumotoy/TFT_ILI9163C).
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License as published by
  11 * the Free Software Foundation; either version 2 of the License, or
  12 * (at your option) any later version.
  13 *
  14 * This program is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17 * GNU General Public License for more details.
  18 */
  19
  20#include <linux/module.h>
  21#include <linux/kernel.h>
  22#include <linux/init.h>
  23#include <linux/gpio.h>
  24#include <linux/delay.h>
  25#include <video/mipi_display.h>
  26
  27#include "fbtft.h"
  28
  29#define DRVNAME         "fb_ili9163"
  30#define WIDTH           128
  31#define HEIGHT          128
  32#define BPP             16
  33#define FPS             30
  34
  35#ifdef GAMMA_ADJ
  36#define GAMMA_LEN       15
  37#define GAMMA_NUM       1
  38#define DEFAULT_GAMMA   "36 29 12 22 1C 15 42 B7 2F 13 12 0A 11 0B 06\n"
  39#endif
  40
  41/* ILI9163C commands */
  42#define CMD_FRMCTR1     0xB1 /* Frame Rate Control */
  43                             /* (In normal mode/Full colors) */
  44#define CMD_FRMCTR2     0xB2 /* Frame Rate Control (In Idle mode/8-colors) */
  45#define CMD_FRMCTR3     0xB3 /* Frame Rate Control */
  46                             /* (In Partial mode/full colors) */
  47#define CMD_DINVCTR     0xB4 /* Display Inversion Control */
  48#define CMD_RGBBLK      0xB5 /* RGB Interface Blanking Porch setting */
  49#define CMD_DFUNCTR     0xB6 /* Display Function set 5 */
  50#define CMD_SDRVDIR     0xB7 /* Source Driver Direction Control */
  51#define CMD_GDRVDIR     0xB8 /* Gate Driver Direction Control  */
  52
  53#define CMD_PWCTR1      0xC0 /* Power_Control1 */
  54#define CMD_PWCTR2      0xC1 /* Power_Control2 */
  55#define CMD_PWCTR3      0xC2 /* Power_Control3 */
  56#define CMD_PWCTR4      0xC3 /* Power_Control4 */
  57#define CMD_PWCTR5      0xC4 /* Power_Control5 */
  58#define CMD_VCOMCTR1    0xC5 /* VCOM_Control 1 */
  59#define CMD_VCOMCTR2    0xC6 /* VCOM_Control 2 */
  60#define CMD_VCOMOFFS    0xC7 /* VCOM Offset Control */
  61#define CMD_PGAMMAC     0xE0 /* Positive Gamma Correction Setting */
  62#define CMD_NGAMMAC     0xE1 /* Negative Gamma Correction Setting */
  63#define CMD_GAMRSEL     0xF2 /* GAM_R_SEL */
  64
  65/*
  66 * This display:
  67 * http://www.ebay.com/itm/Replace-Nokia-5110-LCD-1-44-Red-Serial-128X128-SPI-
  68 * Color-TFT-LCD-Display-Module-/271422122271
  69 * This particular display has a design error! The controller has 3 pins to
  70 * configure to constrain the memory and resolution to a fixed dimension (in
  71 * that case 128x128) but they leaved those pins configured for 128x160 so
  72 * there was several pixel memory addressing problems.
  73 * I solved by setup several parameters that dinamically fix the resolution as
  74 * needit so below the parameters for this display. If you have a strain or a
  75 * correct display (can happen with chinese) you can copy those parameters and
  76 * create setup for different displays.
  77 */
  78
  79#ifdef RED
  80#define __OFFSET                32 /*see note 2 - this is the red version */
  81#else
  82#define __OFFSET                0  /*see note 2 - this is the black version */
  83#endif
  84
  85static int init_display(struct fbtft_par *par)
  86{
  87        par->fbtftops.reset(par);
  88
  89        if (par->gpio.cs != -1)
  90                gpio_set_value(par->gpio.cs, 0);  /* Activate chip */
  91
  92        write_reg(par, MIPI_DCS_SOFT_RESET); /* software reset */
  93        mdelay(500);
  94        write_reg(par, MIPI_DCS_EXIT_SLEEP_MODE); /* exit sleep */
  95        mdelay(5);
  96        write_reg(par, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_16BIT);
  97        /* default gamma curve 3 */
  98        write_reg(par, MIPI_DCS_SET_GAMMA_CURVE, 0x02);
  99#ifdef GAMMA_ADJ
 100        write_reg(par, CMD_GAMRSEL, 0x01); /* Enable Gamma adj */
 101#endif
 102        write_reg(par, MIPI_DCS_ENTER_NORMAL_MODE);
 103        write_reg(par, CMD_DFUNCTR, 0xff, 0x06);
 104        /* Frame Rate Control (In normal mode/Full colors) */
 105        write_reg(par, CMD_FRMCTR1, 0x08, 0x02);
 106        write_reg(par, CMD_DINVCTR, 0x07); /* display inversion  */
 107        /* Set VRH1[4:0] & VC[2:0] for VCI1 & GVDD */
 108        write_reg(par, CMD_PWCTR1, 0x0A, 0x02);
 109        /* Set BT[2:0] for AVDD & VCL & VGH & VGL  */
 110        write_reg(par, CMD_PWCTR2, 0x02);
 111        /* Set VMH[6:0] & VML[6:0] for VOMH & VCOML */
 112        write_reg(par, CMD_VCOMCTR1, 0x50, 0x63);
 113        write_reg(par, CMD_VCOMOFFS, 0);
 114
 115        write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS, 0, 0, 0, WIDTH);
 116        write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS, 0, 0, 0, HEIGHT);
 117
 118        write_reg(par, MIPI_DCS_SET_DISPLAY_ON); /* display ON */
 119        write_reg(par, MIPI_DCS_WRITE_MEMORY_START); /* Memory Write */
 120
 121        return 0;
 122}
 123
 124static void set_addr_win(struct fbtft_par *par, int xs, int ys,
 125                         int xe, int ye)
 126{
 127        switch (par->info->var.rotate) {
 128        case 0:
 129                write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS,
 130                          xs >> 8, xs & 0xff, xe >> 8, xe & 0xff);
 131                write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS,
 132                          (ys + __OFFSET) >> 8, (ys + __OFFSET) & 0xff,
 133                          (ye + __OFFSET) >> 8, (ye + __OFFSET) & 0xff);
 134                break;
 135        case 90:
 136                write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS,
 137                          (xs + __OFFSET) >> 8, (xs + __OFFSET) & 0xff,
 138                          (xe + __OFFSET) >> 8, (xe + __OFFSET) & 0xff);
 139                write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS,
 140                          ys >> 8, ys & 0xff, ye >> 8, ye & 0xff);
 141                break;
 142        case 180:
 143        case 270:
 144                write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS,
 145                          xs >> 8, xs & 0xff, xe >> 8, xe & 0xff);
 146                write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS,
 147                          ys >> 8, ys & 0xff, ye >> 8, ye & 0xff);
 148                break;
 149        default:
 150                /* Fix incorrect setting */
 151                par->info->var.rotate = 0;
 152        }
 153        write_reg(par, MIPI_DCS_WRITE_MEMORY_START);
 154}
 155
 156/*
 157 * 7) MY:  1(bottom to top),    0(top to bottom)    Row Address Order
 158 * 6) MX:  1(R to L),           0(L to R)           Column Address Order
 159 * 5) MV:  1(Exchanged),        0(normal)           Row/Column exchange
 160 * 4) ML:  1(bottom to top),    0(top to bottom)    Vertical Refresh Order
 161 * 3) RGB: 1(BGR),              0(RGB)              Color Space
 162 * 2) MH:  1(R to L),           0(L to R)           Horizontal Refresh Order
 163 * 1)
 164 * 0)
 165 *
 166 *      MY, MX, MV, ML,RGB, MH, D1, D0
 167 *      0 | 0 | 0 | 0 | 1 | 0 | 0 | 0   //normal
 168 *      1 | 0 | 0 | 0 | 1 | 0 | 0 | 0   //Y-Mirror
 169 *      0 | 1 | 0 | 0 | 1 | 0 | 0 | 0   //X-Mirror
 170 *      1 | 1 | 0 | 0 | 1 | 0 | 0 | 0   //X-Y-Mirror
 171 *      0 | 0 | 1 | 0 | 1 | 0 | 0 | 0   //X-Y Exchange
 172 *      1 | 0 | 1 | 0 | 1 | 0 | 0 | 0   //X-Y Exchange, Y-Mirror
 173 *      0 | 1 | 1 | 0 | 1 | 0 | 0 | 0   //XY exchange
 174 *      1 | 1 | 1 | 0 | 1 | 0 | 0 | 0
 175 */
 176static int set_var(struct fbtft_par *par)
 177{
 178        u8 mactrl_data = 0; /* Avoid compiler warning */
 179
 180        switch (par->info->var.rotate) {
 181        case 0:
 182                mactrl_data = 0x08;
 183                break;
 184        case 180:
 185                mactrl_data = 0xC8;
 186                break;
 187        case 270:
 188                mactrl_data = 0xA8;
 189                break;
 190        case 90:
 191                mactrl_data = 0x68;
 192                break;
 193        }
 194
 195        /* Colorspcae */
 196        if (par->bgr)
 197                mactrl_data |= (1 << 2);
 198        write_reg(par, MIPI_DCS_SET_ADDRESS_MODE, mactrl_data);
 199        write_reg(par, MIPI_DCS_WRITE_MEMORY_START);
 200        return 0;
 201}
 202
 203#ifdef GAMMA_ADJ
 204#define CURVE(num, idx)  curves[num * par->gamma.num_values + idx]
 205static int gamma_adj(struct fbtft_par *par, unsigned long *curves)
 206{
 207        unsigned long mask[] = {
 208                0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
 209                0x1f, 0x3f, 0x0f, 0x0f, 0x7f, 0x1f,
 210                0x3F, 0x3F, 0x3F, 0x3F, 0x3F};
 211        int i, j;
 212
 213        for (i = 0; i < GAMMA_NUM; i++)
 214                for (j = 0; j < GAMMA_LEN; j++)
 215                        CURVE(i, j) &= mask[i * par->gamma.num_values + j];
 216
 217        write_reg(par, CMD_PGAMMAC,
 218                  CURVE(0, 0),
 219                  CURVE(0, 1),
 220                  CURVE(0, 2),
 221                  CURVE(0, 3),
 222                  CURVE(0, 4),
 223                  CURVE(0, 5),
 224                  CURVE(0, 6),
 225                  (CURVE(0, 7) << 4) | CURVE(0, 8),
 226                  CURVE(0, 9),
 227                  CURVE(0, 10),
 228                  CURVE(0, 11),
 229                  CURVE(0, 12),
 230                  CURVE(0, 13),
 231                  CURVE(0, 14),
 232                  CURVE(0, 15));
 233
 234        /* Write Data to GRAM mode */
 235        write_reg(par, MIPI_DCS_WRITE_MEMORY_START);
 236
 237        return 0;
 238}
 239
 240#undef CURVE
 241#endif
 242
 243static struct fbtft_display display = {
 244        .regwidth = 8,
 245        .width = WIDTH,
 246        .height = HEIGHT,
 247        .bpp = BPP,
 248        .fps = FPS,
 249#ifdef GAMMA_ADJ
 250        .gamma_num = GAMMA_NUM,
 251        .gamma_len = GAMMA_LEN,
 252        .gamma = DEFAULT_GAMMA,
 253#endif
 254        .fbtftops = {
 255                .init_display = init_display,
 256                .set_addr_win = set_addr_win,
 257                .set_var = set_var,
 258#ifdef GAMMA_ADJ
 259                .set_gamma = gamma_adj,
 260#endif
 261        },
 262};
 263
 264FBTFT_REGISTER_DRIVER(DRVNAME, "ilitek,ili9163", &display);
 265
 266MODULE_ALIAS("spi:" DRVNAME);
 267MODULE_ALIAS("platform:" DRVNAME);
 268MODULE_ALIAS("spi:ili9163");
 269MODULE_ALIAS("platform:ili9163");
 270
 271MODULE_DESCRIPTION("FB driver for the ILI9163 LCD Controller");
 272MODULE_AUTHOR("Kozhevnikov Anatoly");
 273MODULE_LICENSE("GPL");
 274