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	fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
112
113	par->fbtftops.reset(par);
114
115	if (par->gpio.cs != -1)
116		gpio_set_value(par->gpio.cs, 0);  /* Activate chip */
117
118	write_reg(par, CMD_SWRESET); /* software reset */
119	mdelay(500);
120	write_reg(par, CMD_SLPOUT); /* exit sleep */
121	mdelay(5);
122	write_reg(par, CMD_PIXFMT, 0x05); /* Set Color Format 16bit */
123	write_reg(par, CMD_GAMMASET, 0x02); /* default gamma curve 3 */
124#ifdef GAMMA_ADJ
125	write_reg(par, CMD_GAMRSEL, 0x01); /* Enable Gamma adj */
126#endif
127	write_reg(par, CMD_NORML);
128	write_reg(par, CMD_DFUNCTR, 0xff, 0x06);
129	/* Frame Rate Control (In normal mode/Full colors) */
130	write_reg(par, CMD_FRMCTR1, 0x08, 0x02);
131	write_reg(par, CMD_DINVCTR, 0x07); /* display inversion  */
132	/* Set VRH1[4:0] & VC[2:0] for VCI1 & GVDD */
133	write_reg(par, CMD_PWCTR1, 0x0A, 0x02);
134	/* Set BT[2:0] for AVDD & VCL & VGH & VGL  */
135	write_reg(par, CMD_PWCTR2, 0x02);
136	/* Set VMH[6:0] & VML[6:0] for VOMH & VCOML */
137	write_reg(par, CMD_VCOMCTR1, 0x50, 0x63);
138	write_reg(par, CMD_VCOMOFFS, 0);
139
140	write_reg(par, CMD_CLMADRS, 0, 0, 0, WIDTH); /* Set Column Address */
141	write_reg(par, CMD_PGEADRS, 0, 0, 0, HEIGHT); /* Set Page Address */
142
143	write_reg(par, CMD_DISPON); /* display ON */
144	write_reg(par, CMD_RAMWR); /* Memory Write */
145
146	return 0;
147}
148
149static void set_addr_win(struct fbtft_par *par, int xs, int ys,
150				int xe, int ye)
151{
152	fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
153		"%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
154
155	switch (par->info->var.rotate) {
156	case 0:
157		write_reg(par, CMD_CLMADRS, xs >> 8, xs & 0xff, xe >> 8,
158				xe & 0xff);
159		write_reg(par, CMD_PGEADRS,
160				(ys + __OFFSET) >> 8, (ys + __OFFSET) & 0xff,
161				(ye + __OFFSET) >> 8, (ye + __OFFSET) & 0xff);
162		break;
163	case 90:
164		write_reg(par, CMD_CLMADRS,
165				(xs + __OFFSET) >> 8, (xs + __OFFSET) & 0xff,
166				(xe + __OFFSET) >> 8, (xe + __OFFSET) & 0xff);
167		write_reg(par, CMD_PGEADRS, ys >> 8, ys & 0xff, ye >> 8,
168				ye & 0xff);
169		break;
170	case 180:
171	case 270:
172		write_reg(par, CMD_CLMADRS, xs >> 8, xs & 0xff, xe >> 8,
173				xe & 0xff);
174		write_reg(par, CMD_PGEADRS, ys >> 8, ys & 0xff, ye >> 8,
175				ye & 0xff);
176		break;
177	default:
178		par->info->var.rotate = 0; /* Fix incorrect setting */
179	}
180	write_reg(par, CMD_RAMWR); /* Write Data to GRAM mode */
181}
182
183/*
1847) MY:  1(bottom to top),	0(top to bottom)    Row Address Order
1856) MX:  1(R to L),		0(L to R)	    Column Address Order
1865) MV:  1(Exchanged),		0(normal)	    Row/Column exchange
1874) ML:  1(bottom to top),	0(top to bottom)    Vertical Refresh Order
1883) RGB: 1(BGR),			0(RGB)		    Color Space
1892) MH:  1(R to L),		0(L to R)	    Horizontal Refresh Order
1901)
1910)
192
193	MY, MX, MV, ML,RGB, MH, D1, D0
194	0 | 0 | 0 | 0 | 1 | 0 | 0 | 0	//normal
195	1 | 0 | 0 | 0 | 1 | 0 | 0 | 0	//Y-Mirror
196	0 | 1 | 0 | 0 | 1 | 0 | 0 | 0	//X-Mirror
197	1 | 1 | 0 | 0 | 1 | 0 | 0 | 0	//X-Y-Mirror
198	0 | 0 | 1 | 0 | 1 | 0 | 0 | 0	//X-Y Exchange
199	1 | 0 | 1 | 0 | 1 | 0 | 0 | 0	//X-Y Exchange, Y-Mirror
200	0 | 1 | 1 | 0 | 1 | 0 | 0 | 0	//XY exchange
201	1 | 1 | 1 | 0 | 1 | 0 | 0 | 0
202*/
203static int set_var(struct fbtft_par *par)
204{
205	u8 mactrl_data = 0; /* Avoid compiler warning */
206
207	fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
208
209	switch (par->info->var.rotate) {
210	case 0:
211		mactrl_data = 0x08;
212		break;
213	case 180:
214		mactrl_data = 0xC8;
215		break;
216	case 270:
217		mactrl_data = 0xA8;
218		break;
219	case 90:
220		mactrl_data = 0x68;
221		break;
222	}
223
224	/* Colorspcae */
225	if (par->bgr)
226		mactrl_data |= (1 << 2);
227	write_reg(par, CMD_MADCTL, mactrl_data);
228	write_reg(par, CMD_RAMWR); /* Write Data to GRAM mode */
229	return 0;
230}
231
232#ifdef GAMMA_ADJ
233#define CURVE(num, idx)  curves[num*par->gamma.num_values + idx]
234static int gamma_adj(struct fbtft_par *par, unsigned long *curves)
235{
236	unsigned long mask[] = {
237		0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
238		0x1f, 0x3f, 0x0f, 0x0f, 0x7f, 0x1f,
239		0x3F, 0x3F, 0x3F, 0x3F, 0x3F};
240	int i, j;
241
242	fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
243
244	for (i = 0; i < GAMMA_NUM; i++)
245		for (j = 0; j < GAMMA_LEN; j++)
246			CURVE(i, j) &= mask[i*par->gamma.num_values + j];
247
248	write_reg(par, CMD_PGAMMAC,
249				CURVE(0, 0),
250				CURVE(0, 1),
251				CURVE(0, 2),
252				CURVE(0, 3),
253				CURVE(0, 4),
254				CURVE(0, 5),
255				CURVE(0, 6),
256				(CURVE(0, 7) << 4) | CURVE(0, 8),
257				CURVE(0, 9),
258				CURVE(0, 10),
259				CURVE(0, 11),
260				CURVE(0, 12),
261				CURVE(0, 13),
262				CURVE(0, 14),
263				CURVE(0, 15)
264				);
265
266	write_reg(par, CMD_RAMWR); /* Write Data to GRAM mode */
267
268	return 0;
269}
270#undef CURVE
271#endif
272
273static struct fbtft_display display = {
274	.regwidth = 8,
275	.width = WIDTH,
276	.height = HEIGHT,
277	.bpp = BPP,
278	.fps = FPS,
279#ifdef GAMMA_ADJ
280	.gamma_num = GAMMA_NUM,
281	.gamma_len = GAMMA_LEN,
282	.gamma = DEFAULT_GAMMA,
283#endif
284	.fbtftops = {
285		.init_display = init_display,
286		.set_addr_win = set_addr_win,
287		.set_var = set_var,
288#ifdef GAMMA_ADJ
289		.set_gamma = gamma_adj,
290#endif
291	},
292};
293
294FBTFT_REGISTER_DRIVER(DRVNAME, "ilitek,ili9163", &display);
295
296MODULE_ALIAS("spi:" DRVNAME);
297MODULE_ALIAS("platform:" DRVNAME);
298MODULE_ALIAS("spi:ili9163");
299MODULE_ALIAS("platform:ili9163");
300
301MODULE_DESCRIPTION("FB driver for the ILI9163 LCD Controller");
302MODULE_AUTHOR("Kozhevnikov Anatoly");
303MODULE_LICENSE("GPL");
304