File Index
Files and directories
Sign in
FILE

fb_ssd1306.c

File preview with syntax highlighting.

linux-6.19.14/drivers/staging/fbtft/fb_ssd1306.c C
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * FB driver for the SSD1306 OLED Controller
  4 *
  5 * Copyright (C) 2013 Noralf Tronnes
  6 */
  7
  8#include <linux/module.h>
  9#include <linux/kernel.h>
 10#include <linux/init.h>
 11#include <linux/gpio/consumer.h>
 12#include <linux/delay.h>
 13
 14#include "fbtft.h"
 15
 16#define DRVNAME		"fb_ssd1306"
 17#define WIDTH		128
 18#define HEIGHT		64
 19
 20/*
 21 * write_reg() caveat:
 22 *
 23 * This doesn't work because D/C has to be LOW for both values:
 24 * write_reg(par, val1, val2);
 25 *
 26 * Do it like this:
 27 * write_reg(par, val1);
 28 * write_reg(par, val2);
 29 */
 30
 31/* Init sequence taken from the Adafruit SSD1306 Arduino library */
 32static int init_display(struct fbtft_par *par)
 33{
 34	par->fbtftops.reset(par);
 35
 36	if (par->gamma.curves[0] == 0) {
 37		mutex_lock(&par->gamma.lock);
 38		if (par->info->var.yres == 64)
 39			par->gamma.curves[0] = 0xCF;
 40		else
 41			par->gamma.curves[0] = 0x8F;
 42		mutex_unlock(&par->gamma.lock);
 43	}
 44
 45	/* Set Display OFF */
 46	write_reg(par, 0xAE);
 47
 48	/* Set Display Clock Divide Ratio/ Oscillator Frequency */
 49	write_reg(par, 0xD5);
 50	write_reg(par, 0x80);
 51
 52	/* Set Multiplex Ratio */
 53	write_reg(par, 0xA8);
 54	if (par->info->var.yres == 64)
 55		write_reg(par, 0x3F);
 56	else if (par->info->var.yres == 48)
 57		write_reg(par, 0x2F);
 58	else
 59		write_reg(par, 0x1F);
 60
 61	/* Set Display Offset */
 62	write_reg(par, 0xD3);
 63	write_reg(par, 0x0);
 64
 65	/* Set Display Start Line */
 66	write_reg(par, 0x40 | 0x0);
 67
 68	/* Charge Pump Setting */
 69	write_reg(par, 0x8D);
 70	/* A[2] = 1b, Enable charge pump during display on */
 71	write_reg(par, 0x14);
 72
 73	/* Set Memory Addressing Mode */
 74	write_reg(par, 0x20);
 75	/* Vertical addressing mode  */
 76	write_reg(par, 0x01);
 77
 78	/* Set Segment Re-map */
 79	/* column address 127 is mapped to SEG0 */
 80	write_reg(par, 0xA0 | 0x1);
 81
 82	/* Set COM Output Scan Direction */
 83	/* remapped mode. Scan from COM[N-1] to COM0 */
 84	write_reg(par, 0xC8);
 85
 86	/* Set COM Pins Hardware Configuration */
 87	write_reg(par, 0xDA);
 88	if (par->info->var.yres == 64)
 89		/* A[4]=1b, Alternative COM pin configuration */
 90		write_reg(par, 0x12);
 91	else if (par->info->var.yres == 48)
 92		/* A[4]=1b, Alternative COM pin configuration */
 93		write_reg(par, 0x12);
 94	else
 95		/* A[4]=0b, Sequential COM pin configuration */
 96		write_reg(par, 0x02);
 97
 98	/* Set Pre-charge Period */
 99	write_reg(par, 0xD9);
100	write_reg(par, 0xF1);
101
102	/* Set VCOMH Deselect Level */
103	write_reg(par, 0xDB);
104	/* according to the datasheet, this value is out of bounds */
105	write_reg(par, 0x40);
106
107	/* Entire Display ON */
108	/* Resume to RAM content display. Output follows RAM content */
109	write_reg(par, 0xA4);
110
111	/* Set Normal Display
112	 * 0 in RAM: OFF in display panel
113	 * 1 in RAM: ON in display panel
114	 */
115	write_reg(par, 0xA6);
116
117	/* Set Display ON */
118	write_reg(par, 0xAF);
119
120	return 0;
121}
122
123static void set_addr_win_64x48(struct fbtft_par *par)
124{
125	/* Set Column Address */
126	write_reg(par, 0x21);
127	write_reg(par, 0x20);
128	write_reg(par, 0x5F);
129
130	/* Set Page Address */
131	write_reg(par, 0x22);
132	write_reg(par, 0x0);
133	write_reg(par, 0x5);
134}
135
136static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
137{
138	/* Set Lower Column Start Address for Page Addressing Mode */
139	write_reg(par, 0x00 | 0x0);
140	/* Set Higher Column Start Address for Page Addressing Mode */
141	write_reg(par, 0x10 | 0x0);
142	/* Set Display Start Line */
143	write_reg(par, 0x40 | 0x0);
144
145	if (par->info->var.xres == 64 && par->info->var.yres == 48)
146		set_addr_win_64x48(par);
147}
148
149static int blank(struct fbtft_par *par, bool on)
150{
151	if (on)
152		write_reg(par, 0xAE);
153	else
154		write_reg(par, 0xAF);
155	return 0;
156}
157
158/* Gamma is used to control Contrast */
159static int set_gamma(struct fbtft_par *par, u32 *curves)
160{
161	/* apply mask */
162	curves[0] &= 0xFF;
163
164	/* Set Contrast Control for BANK0 */
165	write_reg(par, 0x81);
166	write_reg(par, curves[0]);
167
168	return 0;
169}
170
171static int write_vmem(struct fbtft_par *par, size_t offset, size_t len)
172{
173	u16 *vmem16 = (u16 *)par->info->screen_buffer;
174	u32 xres = par->info->var.xres;
175	u32 yres = par->info->var.yres;
176	u8 *buf = par->txbuf.buf;
177	int x, y, i;
178	int ret = 0;
179
180	for (x = 0; x < xres; x++) {
181		for (y = 0; y < yres / 8; y++) {
182			*buf = 0x00;
183			for (i = 0; i < 8; i++)
184				if (vmem16[(y * 8 + i) * xres + x])
185					*buf |= BIT(i);
186			buf++;
187		}
188	}
189
190	/* Write data */
191	gpiod_set_value(par->gpio.dc, 1);
192	ret = par->fbtftops.write(par, par->txbuf.buf, xres * yres / 8);
193	if (ret < 0)
194		dev_err(par->info->device, "write failed and returned: %d\n",
195			ret);
196
197	return ret;
198}
199
200static struct fbtft_display display = {
201	.regwidth = 8,
202	.width = WIDTH,
203	.height = HEIGHT,
204	.gamma_num = 1,
205	.gamma_len = 1,
206	.gamma = "00",
207	.fbtftops = {
208		.write_vmem = write_vmem,
209		.init_display = init_display,
210		.set_addr_win = set_addr_win,
211		.blank = blank,
212		.set_gamma = set_gamma,
213	},
214};
215
216FBTFT_REGISTER_DRIVER(DRVNAME, "solomon,ssd1306", &display);
217
218MODULE_ALIAS("spi:" DRVNAME);
219MODULE_ALIAS("platform:" DRVNAME);
220MODULE_ALIAS("spi:ssd1306");
221MODULE_ALIAS("platform:ssd1306");
222
223MODULE_DESCRIPTION("SSD1306 OLED Driver");
224MODULE_AUTHOR("Noralf Tronnes");
225MODULE_LICENSE("GPL");
~/ Read-only file preview.