1/*
2 * FB driver for the S6D1121 LCD Controller
3 *
4 * Copyright (C) 2013 Roman Rolinsky
5 *
6 * Based on fb_ili9325.c by Noralf Tronnes
7 * Based on ili9325.c by Jeroen Domburg
8 * Init code from UTFT library by Henning Karlsen
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25#include <linux/module.h>
26#include <linux/kernel.h>
27#include <linux/init.h>
28#include <linux/gpio.h>
29#include <linux/delay.h>
30
31#include "fbtft.h"
32
33#define DRVNAME		"fb_s6d1121"
34#define WIDTH		240
35#define HEIGHT		320
36#define BPP		16
37#define FPS		20
38#define DEFAULT_GAMMA	"26 09 24 2C 1F 23 24 25 22 26 25 23 0D 00\n" \
39			"1C 1A 13 1D 0B 11 12 10 13 15 36 19 00 0D"
40
41static int init_display(struct fbtft_par *par)
42{
43	fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
44
45	par->fbtftops.reset(par);
46
47	if (par->gpio.cs != -1)
48		gpio_set_value(par->gpio.cs, 0);  /* Activate chip */
49
50	/* Initialization sequence from Lib_UTFT */
51
52	write_reg(par, 0x0011, 0x2004);
53	write_reg(par, 0x0013, 0xCC00);
54	write_reg(par, 0x0015, 0x2600);
55	write_reg(par, 0x0014, 0x252A);
56	write_reg(par, 0x0012, 0x0033);
57	write_reg(par, 0x0013, 0xCC04);
58	write_reg(par, 0x0013, 0xCC06);
59	write_reg(par, 0x0013, 0xCC4F);
60	write_reg(par, 0x0013, 0x674F);
61	write_reg(par, 0x0011, 0x2003);
62	write_reg(par, 0x0016, 0x0007);
63	write_reg(par, 0x0002, 0x0013);
64	write_reg(par, 0x0003, 0x0003);
65	write_reg(par, 0x0001, 0x0127);
66	write_reg(par, 0x0008, 0x0303);
67	write_reg(par, 0x000A, 0x000B);
68	write_reg(par, 0x000B, 0x0003);
69	write_reg(par, 0x000C, 0x0000);
70	write_reg(par, 0x0041, 0x0000);
71	write_reg(par, 0x0050, 0x0000);
72	write_reg(par, 0x0060, 0x0005);
73	write_reg(par, 0x0070, 0x000B);
74	write_reg(par, 0x0071, 0x0000);
75	write_reg(par, 0x0078, 0x0000);
76	write_reg(par, 0x007A, 0x0000);
77	write_reg(par, 0x0079, 0x0007);
78	write_reg(par, 0x0007, 0x0051);
79	write_reg(par, 0x0007, 0x0053);
80	write_reg(par, 0x0079, 0x0000);
81
82	write_reg(par, 0x0022); /* Write Data to GRAM */
83
84	return 0;
85}
86
87static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
88{
89	fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
90		"%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
91	switch (par->info->var.rotate) {
92	/* R20h = Horizontal GRAM Start Address */
93	/* R21h = Vertical GRAM Start Address */
94	case 0:
95		write_reg(par, 0x0020, xs);
96		write_reg(par, 0x0021, ys);
97		break;
98	case 180:
99		write_reg(par, 0x0020, WIDTH - 1 - xs);
100		write_reg(par, 0x0021, HEIGHT - 1 - ys);
101		break;
102	case 270:
103		write_reg(par, 0x0020, WIDTH - 1 - ys);
104		write_reg(par, 0x0021, xs);
105		break;
106	case 90:
107		write_reg(par, 0x0020, ys);
108		write_reg(par, 0x0021, HEIGHT - 1 - xs);
109		break;
110	}
111	write_reg(par, 0x0022); /* Write Data to GRAM */
112}
113
114static int set_var(struct fbtft_par *par)
115{
116	fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
117
118	switch (par->info->var.rotate) {
119	/* AM: GRAM update direction */
120	case 0:
121		write_reg(par, 0x03, 0x0003 | (par->bgr << 12));
122		break;
123	case 180:
124		write_reg(par, 0x03, 0x0000 | (par->bgr << 12));
125		break;
126	case 270:
127		write_reg(par, 0x03, 0x000A | (par->bgr << 12));
128		break;
129	case 90:
130		write_reg(par, 0x03, 0x0009 | (par->bgr << 12));
131		break;
132	}
133
134	return 0;
135}
136
137/*
138  Gamma string format:
139    PKP0 PKP1 PKP2 PKP3 PKP4 PKP5 PKP6 PKP7 PKP8 PKP9 PKP10 PKP11 VRP0 VRP1
140    PKN0 PKN1 PKN2 PKN3 PKN4 PKN5 PKN6 PKN7 PRN8 PRN9 PRN10 PRN11 VRN0 VRN1
141*/
142#define CURVE(num, idx)  curves[num*par->gamma.num_values + idx]
143static int set_gamma(struct fbtft_par *par, unsigned long *curves)
144{
145	unsigned long mask[] = {
146		0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
147		0x3f, 0x3f, 0x1f, 0x1f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
148		0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x1f, 0x1f,
149	};
150	int i, j;
151
152	fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
153
154	/* apply mask */
155	for (i = 0; i < 2; i++)
156		for (j = 0; j < 14; j++)
157			CURVE(i, j) &= mask[i*par->gamma.num_values + j];
158
159	write_reg(par, 0x0030, CURVE(0, 1) << 8 | CURVE(0, 0));
160	write_reg(par, 0x0031, CURVE(0, 3) << 8 | CURVE(0, 2));
161	write_reg(par, 0x0032, CURVE(0, 5) << 8 | CURVE(0, 3));
162	write_reg(par, 0x0033, CURVE(0, 7) << 8 | CURVE(0, 6));
163	write_reg(par, 0x0034, CURVE(0, 9) << 8 | CURVE(0, 8));
164	write_reg(par, 0x0035, CURVE(0, 11) << 8 | CURVE(0, 10));
165
166	write_reg(par, 0x0036, CURVE(1, 1) << 8 | CURVE(1, 0));
167	write_reg(par, 0x0037, CURVE(1, 3) << 8 | CURVE(1, 2));
168	write_reg(par, 0x0038, CURVE(1, 5) << 8 | CURVE(1, 4));
169	write_reg(par, 0x0039, CURVE(1, 7) << 8 | CURVE(1, 6));
170	write_reg(par, 0x003A, CURVE(1, 9) << 8 | CURVE(1, 8));
171	write_reg(par, 0x003B, CURVE(1, 11) << 8 | CURVE(1, 10));
172
173	write_reg(par, 0x003C, CURVE(0, 13) << 8 | CURVE(0, 12));
174	write_reg(par, 0x003D, CURVE(1, 13) << 8 | CURVE(1, 12));
175
176	return 0;
177}
178#undef CURVE
179
180
181static struct fbtft_display display = {
182	.regwidth = 16,
183	.width = WIDTH,
184	.height = HEIGHT,
185	.bpp = BPP,
186	.fps = FPS,
187	.gamma_num = 2,
188	.gamma_len = 14,
189	.gamma = DEFAULT_GAMMA,
190	.fbtftops = {
191		.init_display = init_display,
192		.set_addr_win = set_addr_win,
193		.set_var = set_var,
194		.set_gamma = set_gamma,
195	},
196};
197FBTFT_REGISTER_DRIVER(DRVNAME, "samsung,s6d1121", &display);
198
199MODULE_ALIAS("spi:" DRVNAME);
200MODULE_ALIAS("platform:" DRVNAME);
201MODULE_ALIAS("spi:s6d1121");
202MODULE_ALIAS("platform:s6d1121");
203
204MODULE_DESCRIPTION("FB driver for the S6D1121 LCD Controller");
205MODULE_AUTHOR("Roman Rolinsky");
206MODULE_LICENSE("GPL");
207