2 * FB driver for the Watterott LCD Controller
4 * Copyright (C) 2013 Noralf Tronnes
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
17 #include <linux/module.h>
18 #include <linux/kernel.h>
19 #include <linux/init.h>
20 #include <linux/gpio.h>
21 #include <linux/delay.h>
25 #define DRVNAME "fb_watterott"
30 #define DEFAULT_BRIGHTNESS 50
32 #define CMD_VERSION 0x01
33 #define CMD_LCD_LED 0x10
34 #define CMD_LCD_RESET 0x11
35 #define CMD_LCD_ORIENTATION 0x20
36 #define CMD_LCD_DRAWIMAGE 0x27
37 #define COLOR_RGB323 8
38 #define COLOR_RGB332 9
39 #define COLOR_RGB233 10
40 #define COLOR_RGB565 16
42 static short mode
= 565;
43 module_param(mode
, short, 0000);
44 MODULE_PARM_DESC(mode
, "RGB color transfer mode: 332, 565 (default)");
46 static void write_reg8_bus8(struct fbtft_par
*par
, int len
, ...)
53 for (i
= 0; i
< len
; i
++)
54 *buf
++ = (u8
)va_arg(args
, unsigned int);
57 fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER
, par
,
58 par
->info
->device
, u8
, par
->buf
, len
, "%s: ", __func__
);
60 ret
= par
->fbtftops
.write(par
, par
->buf
, len
);
62 dev_err(par
->info
->device
,
63 "write() failed and returned %d\n", ret
);
68 static int write_vmem(struct fbtft_par
*par
, size_t offset
, size_t len
)
70 unsigned int start_line
, end_line
;
71 u16
*vmem16
= (u16
*)(par
->info
->screen_buffer
+ offset
);
72 __be16
*pos
= par
->txbuf
.buf
+ 1;
73 __be16
*buf16
= par
->txbuf
.buf
+ 10;
77 start_line
= offset
/ par
->info
->fix
.line_length
;
78 end_line
= start_line
+ (len
/ par
->info
->fix
.line_length
) - 1;
80 /* Set command header. pos: x, y, w, h */
81 ((u8
*)par
->txbuf
.buf
)[0] = CMD_LCD_DRAWIMAGE
;
83 pos
[2] = cpu_to_be16(par
->info
->var
.xres
);
84 pos
[3] = cpu_to_be16(1);
85 ((u8
*)par
->txbuf
.buf
)[9] = COLOR_RGB565
;
87 for (i
= start_line
; i
<= end_line
; i
++) {
88 pos
[1] = cpu_to_be16(i
);
89 for (j
= 0; j
< par
->info
->var
.xres
; j
++)
90 buf16
[j
] = cpu_to_be16(*vmem16
++);
91 ret
= par
->fbtftops
.write(par
,
92 par
->txbuf
.buf
, 10 + par
->info
->fix
.line_length
);
101 #define RGB565toRGB323(c) (((c&0xE000)>>8) | ((c&0600)>>6) | ((c&0x001C)>>2))
102 #define RGB565toRGB332(c) (((c&0xE000)>>8) | ((c&0700)>>6) | ((c&0x0018)>>3))
103 #define RGB565toRGB233(c) (((c&0xC000)>>8) | ((c&0700)>>5) | ((c&0x001C)>>2))
105 static int write_vmem_8bit(struct fbtft_par
*par
, size_t offset
, size_t len
)
107 unsigned int start_line
, end_line
;
108 u16
*vmem16
= (u16
*)(par
->info
->screen_buffer
+ offset
);
109 __be16
*pos
= par
->txbuf
.buf
+ 1;
110 u8
*buf8
= par
->txbuf
.buf
+ 10;
114 start_line
= offset
/ par
->info
->fix
.line_length
;
115 end_line
= start_line
+ (len
/ par
->info
->fix
.line_length
) - 1;
117 /* Set command header. pos: x, y, w, h */
118 ((u8
*)par
->txbuf
.buf
)[0] = CMD_LCD_DRAWIMAGE
;
120 pos
[2] = cpu_to_be16(par
->info
->var
.xres
);
121 pos
[3] = cpu_to_be16(1);
122 ((u8
*)par
->txbuf
.buf
)[9] = COLOR_RGB332
;
124 for (i
= start_line
; i
<= end_line
; i
++) {
125 pos
[1] = cpu_to_be16(i
);
126 for (j
= 0; j
< par
->info
->var
.xres
; j
++) {
127 buf8
[j
] = RGB565toRGB332(*vmem16
);
130 ret
= par
->fbtftops
.write(par
,
131 par
->txbuf
.buf
, 10 + par
->info
->var
.xres
);
140 static unsigned int firmware_version(struct fbtft_par
*par
)
144 write_reg(par
, CMD_VERSION
);
145 par
->fbtftops
.read(par
, rxbuf
, 4);
149 return (rxbuf
[0] - '0') << 8 | (rxbuf
[2] - '0') << 4 | (rxbuf
[3] - '0');
152 static int init_display(struct fbtft_par
*par
)
155 unsigned int version
;
158 /* enable SPI interface by having CS and MOSI low during reset */
159 save_mode
= par
->spi
->mode
;
160 par
->spi
->mode
|= SPI_CS_HIGH
;
161 ret
= spi_setup(par
->spi
); /* set CS inactive low */
163 dev_err(par
->info
->device
, "Could not set SPI_CS_HIGH\n");
166 write_reg(par
, 0x00); /* make sure mode is set */
169 par
->fbtftops
.reset(par
);
171 par
->spi
->mode
= save_mode
;
172 ret
= spi_setup(par
->spi
);
174 dev_err(par
->info
->device
, "Could not restore SPI mode\n");
177 write_reg(par
, 0x00);
179 version
= firmware_version(par
);
180 fbtft_par_dbg(DEBUG_INIT_DISPLAY
, par
, "Firmware version: %x.%02x\n",
181 version
>> 8, version
& 0xFF);
184 par
->fbtftops
.write_vmem
= write_vmem_8bit
;
188 static void set_addr_win(struct fbtft_par
*par
, int xs
, int ys
, int xe
, int ye
)
190 /* not used on this controller */
193 static int set_var(struct fbtft_par
*par
)
197 /* this controller rotates clock wise */
198 switch (par
->info
->var
.rotate
) {
211 write_reg(par
, CMD_LCD_ORIENTATION
, rotate
);
216 static int verify_gpios(struct fbtft_par
*par
)
218 if (par
->gpio
.reset
< 0) {
219 dev_err(par
->info
->device
, "Missing 'reset' gpio. Aborting.\n");
225 #ifdef CONFIG_FB_BACKLIGHT
226 static int backlight_chip_update_status(struct backlight_device
*bd
)
228 struct fbtft_par
*par
= bl_get_data(bd
);
229 int brightness
= bd
->props
.brightness
;
231 fbtft_par_dbg(DEBUG_BACKLIGHT
, par
,
232 "%s: brightness=%d, power=%d, fb_blank=%d\n",
233 __func__
, bd
->props
.brightness
, bd
->props
.power
,
236 if (bd
->props
.power
!= FB_BLANK_UNBLANK
)
239 if (bd
->props
.fb_blank
!= FB_BLANK_UNBLANK
)
242 write_reg(par
, CMD_LCD_LED
, brightness
);
247 static const struct backlight_ops bl_ops
= {
248 .update_status
= backlight_chip_update_status
,
251 static void register_chip_backlight(struct fbtft_par
*par
)
253 struct backlight_device
*bd
;
254 struct backlight_properties bl_props
= { 0, };
256 bl_props
.type
= BACKLIGHT_RAW
;
257 bl_props
.power
= FB_BLANK_POWERDOWN
;
258 bl_props
.max_brightness
= 100;
259 bl_props
.brightness
= DEFAULT_BRIGHTNESS
;
261 bd
= backlight_device_register(dev_driver_string(par
->info
->device
),
262 par
->info
->device
, par
, &bl_ops
, &bl_props
);
264 dev_err(par
->info
->device
,
265 "cannot register backlight device (%ld)\n",
269 par
->info
->bl_dev
= bd
;
271 if (!par
->fbtftops
.unregister_backlight
)
272 par
->fbtftops
.unregister_backlight
= fbtft_unregister_backlight
;
275 #define register_chip_backlight NULL
278 static struct fbtft_display display
= {
284 .txbuflen
= TXBUFLEN
,
286 .write_register
= write_reg8_bus8
,
287 .write_vmem
= write_vmem
,
288 .init_display
= init_display
,
289 .set_addr_win
= set_addr_win
,
291 .verify_gpios
= verify_gpios
,
292 .register_backlight
= register_chip_backlight
,
296 FBTFT_REGISTER_DRIVER(DRVNAME
, "watterott,openlcd", &display
);
298 MODULE_ALIAS("spi:" DRVNAME
);
300 MODULE_DESCRIPTION("FB driver for the Watterott LCD Controller");
301 MODULE_AUTHOR("Noralf Tronnes");
302 MODULE_LICENSE("GPL");