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
26#include "fbtft.h"
27
28#define DRVNAME		"fb_ili9163"
29#define WIDTH		128
30#define HEIGHT		128
31#define BPP		16
32#define FPS		30
33
34#ifdef GAMMA_ADJ
35#define GAMMA_LEN	15
36#define GAMMA_NUM	1
37#define DEFAULT_GAMMA	"36 29 12 22 1C 15 42 B7 2F 13 12 0A 11 0B 06\n"
38#endif
39
40/* ILI9163C commands */
41#define CMD_NOP		0x00 /* Non operation*/
42#define CMD_SWRESET	0x01 /* Soft Reset */
43#define CMD_SLPIN	0x10 /* Sleep ON */
44#define CMD_SLPOUT	0x11 /* Sleep OFF */
45#define CMD_PTLON	0x12 /* Partial Mode ON */
46#define CMD_NORML	0x13 /* Normal Display ON */
47#define CMD_DINVOF	0x20 /* Display Inversion OFF */
48#define CMD_DINVON	0x21 /* Display Inversion ON */
49#define CMD_GAMMASET	0x26 /* Gamma Set (0x01[1],0x02[2],0x04[3],0x08[4]) */
50#define CMD_DISPOFF	0x28 /* Display OFF */
51#define CMD_DISPON	0x29 /* Display ON */
52#define CMD_IDLEON	0x39 /* Idle Mode ON */
53#define CMD_IDLEOF	0x38 /* Idle Mode OFF */
54#define CMD_CLMADRS	0x2A /* Column Address Set */
55#define CMD_PGEADRS	0x2B /* Page Address Set */
56
57#define CMD_RAMWR	0x2C /* Memory Write */
58#define CMD_RAMRD	0x2E /* Memory Read */
59#define CMD_CLRSPACE	0x2D /* Color Space : 4K/65K/262K */
60#define CMD_PARTAREA	0x30 /* Partial Area */
61#define CMD_VSCLLDEF	0x33 /* Vertical Scroll Definition */
62#define CMD_TEFXLON	0x34 /* Tearing Effect Line ON */
63#define CMD_TEFXLOF	0x35 /* Tearing Effect Line OFF */
64#define CMD_MADCTL	0x36 /* Memory Access Control */
65
66#define CMD_PIXFMT	0x3A /* Interface Pixel Format */
67#define CMD_FRMCTR1	0xB1 /* Frame Rate Control
68				(In normal mode/Full colors) */
69#define CMD_FRMCTR2	0xB2 /* Frame Rate Control (In Idle mode/8-colors) */
70#define CMD_FRMCTR3	0xB3 /* Frame Rate Control
71				(In Partial mode/full colors) */
72#define CMD_DINVCTR	0xB4 /* Display Inversion Control */
73#define CMD_RGBBLK	0xB5 /* RGB Interface Blanking Porch setting */
74#define CMD_DFUNCTR	0xB6 /* Display Function set 5 */
75#define CMD_SDRVDIR	0xB7 /* Source Driver Direction Control */
76#define CMD_GDRVDIR	0xB8 /* Gate Driver Direction Control  */
77
78#define CMD_PWCTR1	0xC0 /* Power_Control1 */
79#define CMD_PWCTR2	0xC1 /* Power_Control2 */
80#define CMD_PWCTR3	0xC2 /* Power_Control3 */
81#define CMD_PWCTR4	0xC3 /* Power_Control4 */
82#define CMD_PWCTR5	0xC4 /* Power_Control5 */
83#define CMD_VCOMCTR1	0xC5 /* VCOM_Control 1 */
84#define CMD_VCOMCTR2	0xC6 /* VCOM_Control 2 */
85#define CMD_VCOMOFFS	0xC7 /* VCOM Offset Control */
86#define CMD_PGAMMAC	0xE0 /* Positive Gamma Correction Setting */
87#define CMD_NGAMMAC	0xE1 /* Negative Gamma Correction Setting */
88#define CMD_GAMRSEL	0xF2 /* GAM_R_SEL */
89
90/*
91This display:
92http://www.ebay.com/itm/Replace-Nokia-5110-LCD-1-44-Red-Serial-128X128-SPI-Color-TFT-LCD-Display-Module-/271422122271
93This particular display has a design error! The controller has 3 pins to
94configure to constrain the memory and resolution to a fixed dimension (in
95that case 128x128) but they leaved those pins configured for 128x160 so
96there was several pixel memory addressing problems.
97I solved by setup several parameters that dinamically fix the resolution as
98needit so below the parameters for this display. If you have a strain or a
99correct display (can happen with chinese) you can copy those parameters and
100create setup for different displays.
101*/
102
103#ifdef RED
104#define __OFFSET		32 /*see note 2 - this is the red version */
105#else
106#define __OFFSET		0  /*see note 2 - this is the black version */
107#endif
108
109static int init_display(struct fbtft_par *par)
110{
111	par->fbtftops.reset(par);
112
113	if (par->gpio.cs != -1)
114		gpio_set_value(par->gpio.cs, 0);  /* Activate chip */
115
116	write_reg(par, CMD_SWRESET); /* software reset */
117	mdelay(500);
118	write_reg(par, CMD_SLPOUT); /* exit sleep */
119	mdelay(5);
120	write_reg(par, CMD_PIXFMT, 0x05); /* Set Color Format 16bit */
121	write_reg(par, CMD_GAMMASET, 0x02); /* default gamma curve 3 */
122#ifdef GAMMA_ADJ
123	write_reg(par, CMD_GAMRSEL, 0x01); /* Enable Gamma adj */
124#endif
125	write_reg(par, CMD_NORML);
126	write_reg(par, CMD_DFUNCTR, 0xff, 0x06);
127	/* Frame Rate Control (In normal mode/Full colors) */
128	write_reg(par, CMD_FRMCTR1, 0x08, 0x02);
129	write_reg(par, CMD_DINVCTR, 0x07); /* display inversion  */
130	/* Set VRH1[4:0] & VC[2:0] for VCI1 & GVDD */
131	write_reg(par, CMD_PWCTR1, 0x0A, 0x02);
132	/* Set BT[2:0] for AVDD & VCL & VGH & VGL  */
133	write_reg(par, CMD_PWCTR2, 0x02);
134	/* Set VMH[6:0] & VML[6:0] for VOMH & VCOML */
135	write_reg(par, CMD_VCOMCTR1, 0x50, 0x63);
136	write_reg(par, CMD_VCOMOFFS, 0);
137
138	write_reg(par, CMD_CLMADRS, 0, 0, 0, WIDTH); /* Set Column Address */
139	write_reg(par, CMD_PGEADRS, 0, 0, 0, HEIGHT); /* Set Page Address */
140
141	write_reg(par, CMD_DISPON); /* display ON */
142	write_reg(par, CMD_RAMWR); /* Memory Write */
143
144	return 0;
145}
146
147static void set_addr_win(struct fbtft_par *par, int xs, int ys,
148				int xe, int ye)
149{
150	switch (par->info->var.rotate) {
151	case 0:
152		write_reg(par, CMD_CLMADRS, xs >> 8, xs & 0xff, xe >> 8,
153				xe & 0xff);
154		write_reg(par, CMD_PGEADRS,
155				(ys + __OFFSET) >> 8, (ys + __OFFSET) & 0xff,
156				(ye + __OFFSET) >> 8, (ye + __OFFSET) & 0xff);
157		break;
158	case 90:
159		write_reg(par, CMD_CLMADRS,
160				(xs + __OFFSET) >> 8, (xs + __OFFSET) & 0xff,
161				(xe + __OFFSET) >> 8, (xe + __OFFSET) & 0xff);
162		write_reg(par, CMD_PGEADRS, ys >> 8, ys & 0xff, ye >> 8,
163				ye & 0xff);
164		break;
165	case 180:
166	case 270:
167		write_reg(par, CMD_CLMADRS, xs >> 8, xs & 0xff, xe >> 8,
168				xe & 0xff);
169		write_reg(par, CMD_PGEADRS, ys >> 8, ys & 0xff, ye >> 8,
170				ye & 0xff);
171		break;
172	default:
173		par->info->var.rotate = 0; /* Fix incorrect setting */
174	}
175	write_reg(par, CMD_RAMWR); /* Write Data to GRAM mode */
176}
177
178/*
1797) MY:  1(bottom to top),	0(top to bottom)    Row Address Order
1806) MX:  1(R to L),		0(L to R)	    Column Address Order
1815) MV:  1(Exchanged),		0(normal)	    Row/Column exchange
1824) ML:  1(bottom to top),	0(top to bottom)    Vertical Refresh Order
1833) RGB: 1(BGR),			0(RGB)		    Color Space
1842) MH:  1(R to L),		0(L to R)	    Horizontal Refresh Order
1851)
1860)
187
188	MY, MX, MV, ML,RGB, MH, D1, D0
189	0 | 0 | 0 | 0 | 1 | 0 | 0 | 0	//normal
190	1 | 0 | 0 | 0 | 1 | 0 | 0 | 0	//Y-Mirror
191	0 | 1 | 0 | 0 | 1 | 0 | 0 | 0	//X-Mirror
192	1 | 1 | 0 | 0 | 1 | 0 | 0 | 0	//X-Y-Mirror
193	0 | 0 | 1 | 0 | 1 | 0 | 0 | 0	//X-Y Exchange
194	1 | 0 | 1 | 0 | 1 | 0 | 0 | 0	//X-Y Exchange, Y-Mirror
195	0 | 1 | 1 | 0 | 1 | 0 | 0 | 0	//XY exchange
196	1 | 1 | 1 | 0 | 1 | 0 | 0 | 0
197*/
198static int set_var(struct fbtft_par *par)
199{
200	u8 mactrl_data = 0; /* Avoid compiler warning */
201
202	switch (par->info->var.rotate) {
203	case 0:
204		mactrl_data = 0x08;
205		break;
206	case 180:
207		mactrl_data = 0xC8;
208		break;
209	case 270:
210		mactrl_data = 0xA8;
211		break;
212	case 90:
213		mactrl_data = 0x68;
214		break;
215	}
216
217	/* Colorspcae */
218	if (par->bgr)
219		mactrl_data |= (1 << 2);
220	write_reg(par, CMD_MADCTL, mactrl_data);
221	write_reg(par, CMD_RAMWR); /* Write Data to GRAM mode */
222	return 0;
223}
224
225#ifdef GAMMA_ADJ
226#define CURVE(num, idx)  curves[num * par->gamma.num_values + idx]
227static int gamma_adj(struct fbtft_par *par, unsigned long *curves)
228{
229	unsigned long mask[] = {
230		0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
231		0x1f, 0x3f, 0x0f, 0x0f, 0x7f, 0x1f,
232		0x3F, 0x3F, 0x3F, 0x3F, 0x3F};
233	int i, j;
234
235	for (i = 0; i < GAMMA_NUM; i++)
236		for (j = 0; j < GAMMA_LEN; j++)
237			CURVE(i, j) &= mask[i * par->gamma.num_values + j];
238
239	write_reg(par, CMD_PGAMMAC,
240				CURVE(0, 0),
241				CURVE(0, 1),
242				CURVE(0, 2),
243				CURVE(0, 3),
244				CURVE(0, 4),
245				CURVE(0, 5),
246				CURVE(0, 6),
247				(CURVE(0, 7) << 4) | CURVE(0, 8),
248				CURVE(0, 9),
249				CURVE(0, 10),
250				CURVE(0, 11),
251				CURVE(0, 12),
252				CURVE(0, 13),
253				CURVE(0, 14),
254				CURVE(0, 15)
255				);
256
257	write_reg(par, CMD_RAMWR); /* Write Data to GRAM mode */
258
259	return 0;
260}
261#undef CURVE
262#endif
263
264static struct fbtft_display display = {
265	.regwidth = 8,
266	.width = WIDTH,
267	.height = HEIGHT,
268	.bpp = BPP,
269	.fps = FPS,
270#ifdef GAMMA_ADJ
271	.gamma_num = GAMMA_NUM,
272	.gamma_len = GAMMA_LEN,
273	.gamma = DEFAULT_GAMMA,
274#endif
275	.fbtftops = {
276		.init_display = init_display,
277		.set_addr_win = set_addr_win,
278		.set_var = set_var,
279#ifdef GAMMA_ADJ
280		.set_gamma = gamma_adj,
281#endif
282	},
283};
284
285FBTFT_REGISTER_DRIVER(DRVNAME, "ilitek,ili9163", &display);
286
287MODULE_ALIAS("spi:" DRVNAME);
288MODULE_ALIAS("platform:" DRVNAME);
289MODULE_ALIAS("spi:ili9163");
290MODULE_ALIAS("platform:ili9163");
291
292MODULE_DESCRIPTION("FB driver for the ILI9163 LCD Controller");
293MODULE_AUTHOR("Kozhevnikov Anatoly");
294MODULE_LICENSE("GPL");
295