cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

fb_sh1106.c (4132B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * FB driver for the SH1106 OLED Controller
      4 * Based on the SSD1306 driver by Noralf Tronnes
      5 *
      6 * Copyright (C) 2017 Heiner Kallweit
      7 */
      8
      9#include <linux/module.h>
     10#include <linux/kernel.h>
     11#include <linux/init.h>
     12#include <linux/delay.h>
     13
     14#include "fbtft.h"
     15
     16#define DRVNAME		"fb_sh1106"
     17#define WIDTH		128
     18#define HEIGHT		64
     19
     20/* Init sequence based on the Adafruit SSD1306 Arduino library */
     21static int init_display(struct fbtft_par *par)
     22{
     23	if (!par->info->var.xres || par->info->var.xres > WIDTH ||
     24	    !par->info->var.yres || par->info->var.yres > HEIGHT ||
     25	    par->info->var.yres % 8) {
     26		dev_err(par->info->device, "Invalid screen size\n");
     27		return -EINVAL;
     28	}
     29
     30	if (par->info->var.rotate) {
     31		dev_err(par->info->device, "Display rotation not supported\n");
     32		return -EINVAL;
     33	}
     34
     35	par->fbtftops.reset(par);
     36
     37	/* Set Display OFF */
     38	write_reg(par, 0xAE);
     39
     40	/* Set Display Clock Divide Ratio/ Oscillator Frequency */
     41	write_reg(par, 0xD5, 0x80);
     42
     43	/* Set Multiplex Ratio */
     44	write_reg(par, 0xA8, par->info->var.yres - 1);
     45
     46	/* Set Display Offset */
     47	write_reg(par, 0xD3, 0x00);
     48
     49	/* Set Display Start Line */
     50	write_reg(par, 0x40 | 0x0);
     51
     52	/* Set Segment Re-map */
     53	/* column address 127 is mapped to SEG0 */
     54	write_reg(par, 0xA0 | 0x1);
     55
     56	/* Set COM Output Scan Direction */
     57	/* remapped mode. Scan from COM[N-1] to COM0 */
     58	write_reg(par, 0xC8);
     59
     60	/* Set COM Pins Hardware Configuration */
     61	if (par->info->var.yres == 64)
     62		/* A[4]=1b, Alternative COM pin configuration */
     63		write_reg(par, 0xDA, 0x12);
     64	else if (par->info->var.yres == 48)
     65		/* A[4]=1b, Alternative COM pin configuration */
     66		write_reg(par, 0xDA, 0x12);
     67	else
     68		/* A[4]=0b, Sequential COM pin configuration */
     69		write_reg(par, 0xDA, 0x02);
     70
     71	/* Set Pre-charge Period */
     72	write_reg(par, 0xD9, 0xF1);
     73
     74	/* Set VCOMH Deselect Level */
     75	write_reg(par, 0xDB, 0x40);
     76
     77	/* Set Display ON */
     78	write_reg(par, 0xAF);
     79
     80	msleep(150);
     81
     82	return 0;
     83}
     84
     85static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
     86{
     87}
     88
     89static int blank(struct fbtft_par *par, bool on)
     90{
     91	fbtft_par_dbg(DEBUG_BLANK, par, "(%s=%s)\n",
     92		      __func__, on ? "true" : "false");
     93
     94	write_reg(par, on ? 0xAE : 0xAF);
     95
     96	return 0;
     97}
     98
     99/* Gamma is used to control Contrast */
    100static int set_gamma(struct fbtft_par *par, u32 *curves)
    101{
    102	/* apply mask */
    103	curves[0] &= 0xFF;
    104
    105	/* Set Contrast Control for BANK0 */
    106	write_reg(par, 0x81, curves[0]);
    107
    108	return 0;
    109}
    110
    111static int write_vmem(struct fbtft_par *par, size_t offset, size_t len)
    112{
    113	u16 *vmem16 = (u16 *)par->info->screen_buffer;
    114	u32 xres = par->info->var.xres;
    115	int page, page_start, page_end, x, i, ret;
    116	u8 *buf = par->txbuf.buf;
    117
    118	/* offset refers to vmem with 2 bytes element size */
    119	page_start = offset / (8 * 2 * xres);
    120	page_end = DIV_ROUND_UP(offset + len, 8 * 2 * xres);
    121
    122	for (page = page_start; page < page_end; page++) {
    123		/* set page and set column to 2 because of vidmem width 132 */
    124		write_reg(par, 0xb0 | page, 0x00 | 2, 0x10 | 0);
    125
    126		memset(buf, 0, xres);
    127		for (x = 0; x < xres; x++)
    128			for (i = 0; i < 8; i++)
    129				if (vmem16[(page * 8 + i) * xres + x])
    130					buf[x] |= BIT(i);
    131
    132		/* Write data */
    133		ret = fbtft_write_buf_dc(par, buf, xres, 1);
    134		if (ret < 0)
    135			return ret;
    136	}
    137
    138	return 0;
    139}
    140
    141static void write_register(struct fbtft_par *par, int len, ...)
    142{
    143	va_list args;
    144	int i;
    145
    146	va_start(args, len);
    147
    148	for (i = 0; i < len; i++)
    149		par->buf[i] = va_arg(args, unsigned int);
    150
    151	/* keep DC low for all command bytes to transfer */
    152	fbtft_write_buf_dc(par, par->buf, len, 0);
    153
    154	va_end(args);
    155}
    156
    157static struct fbtft_display display = {
    158	.regwidth = 8,
    159	.width = WIDTH,
    160	.height = HEIGHT,
    161	.txbuflen = WIDTH,
    162	.gamma_num = 1,
    163	.gamma_len = 1,
    164	/* set default contrast to 0xcd = 80% */
    165	.gamma = "cd",
    166	.fbtftops = {
    167		.write_vmem = write_vmem,
    168		.write_register = write_register,
    169		.init_display = init_display,
    170		.set_addr_win = set_addr_win,
    171		.blank = blank,
    172		.set_gamma = set_gamma,
    173	},
    174};
    175
    176FBTFT_REGISTER_SPI_DRIVER(DRVNAME, "sinowealth", "sh1106", &display);
    177
    178MODULE_DESCRIPTION("SH1106 OLED Driver");
    179MODULE_AUTHOR("Heiner Kallweit");
    180MODULE_LICENSE("GPL");