^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Driver for the Solomon SSD1307 OLED controller
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright 2012 Free Electrons
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) #include <linux/backlight.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/fb.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/gpio/consumer.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/i2c.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/property.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/pwm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/uaccess.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/regulator/consumer.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #define SSD1307FB_DATA 0x40
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #define SSD1307FB_COMMAND 0x80
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #define SSD1307FB_SET_ADDRESS_MODE 0x20
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #define SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL (0x00)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #define SSD1307FB_SET_ADDRESS_MODE_VERTICAL (0x01)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #define SSD1307FB_SET_ADDRESS_MODE_PAGE (0x02)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #define SSD1307FB_SET_COL_RANGE 0x21
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #define SSD1307FB_SET_PAGE_RANGE 0x22
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) #define SSD1307FB_CONTRAST 0x81
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) #define SSD1307FB_SET_LOOKUP_TABLE 0x91
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) #define SSD1307FB_CHARGE_PUMP 0x8d
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) #define SSD1307FB_SEG_REMAP_ON 0xa1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) #define SSD1307FB_DISPLAY_OFF 0xae
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) #define SSD1307FB_SET_MULTIPLEX_RATIO 0xa8
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) #define SSD1307FB_DISPLAY_ON 0xaf
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) #define SSD1307FB_START_PAGE_ADDRESS 0xb0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) #define SSD1307FB_SET_DISPLAY_OFFSET 0xd3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) #define SSD1307FB_SET_CLOCK_FREQ 0xd5
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) #define SSD1307FB_SET_AREA_COLOR_MODE 0xd8
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) #define SSD1307FB_SET_PRECHARGE_PERIOD 0xd9
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) #define SSD1307FB_SET_COM_PINS_CONFIG 0xda
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) #define SSD1307FB_SET_VCOMH 0xdb
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) #define MAX_CONTRAST 255
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) #define REFRESHRATE 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) static u_int refreshrate = REFRESHRATE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) module_param(refreshrate, uint, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) struct ssd1307fb_deviceinfo {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) u32 default_vcomh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) u32 default_dclk_div;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) u32 default_dclk_frq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) int need_pwm;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) int need_chargepump;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) struct ssd1307fb_par {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) unsigned area_color_enable : 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) unsigned com_invdir : 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) unsigned com_lrremap : 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) unsigned com_seq : 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) unsigned lookup_table_set : 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) unsigned low_power : 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) unsigned seg_remap : 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) u32 com_offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) u32 contrast;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) u32 dclk_div;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) u32 dclk_frq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) const struct ssd1307fb_deviceinfo *device_info;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) struct i2c_client *client;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) u32 height;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) struct fb_info *info;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) u8 lookup_table[4];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) u32 page_offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) u32 col_offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) u32 prechargep1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) u32 prechargep2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) struct pwm_device *pwm;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) struct gpio_desc *reset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) struct regulator *vbat_reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) u32 vcomh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) u32 width;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) struct ssd1307fb_array {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) u8 type;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) u8 data[];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) static const struct fb_fix_screeninfo ssd1307fb_fix = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) .id = "Solomon SSD1307",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) .type = FB_TYPE_PACKED_PIXELS,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) .visual = FB_VISUAL_MONO10,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) .xpanstep = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) .ypanstep = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) .ywrapstep = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) .accel = FB_ACCEL_NONE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) static const struct fb_var_screeninfo ssd1307fb_var = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) .bits_per_pixel = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) .red = { .length = 1 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) .green = { .length = 1 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) .blue = { .length = 1 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) static struct ssd1307fb_array *ssd1307fb_alloc_array(u32 len, u8 type)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) struct ssd1307fb_array *array;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) array = kzalloc(sizeof(struct ssd1307fb_array) + len, GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) if (!array)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) return NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) array->type = type;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) return array;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) static int ssd1307fb_write_array(struct i2c_client *client,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) struct ssd1307fb_array *array, u32 len)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) len += sizeof(struct ssd1307fb_array);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) ret = i2c_master_send(client, (u8 *)array, len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) if (ret != len) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) dev_err(&client->dev, "Couldn't send I2C command.\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) static inline int ssd1307fb_write_cmd(struct i2c_client *client, u8 cmd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) struct ssd1307fb_array *array;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) array = ssd1307fb_alloc_array(1, SSD1307FB_COMMAND);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) if (!array)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) array->data[0] = cmd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) ret = ssd1307fb_write_array(client, array, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) kfree(array);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) static void ssd1307fb_update_display(struct ssd1307fb_par *par)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) struct ssd1307fb_array *array;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) u8 *vmem = par->info->screen_buffer;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) unsigned int line_length = par->info->fix.line_length;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) unsigned int pages = DIV_ROUND_UP(par->height, 8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) int i, j, k;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) array = ssd1307fb_alloc_array(par->width * pages, SSD1307FB_DATA);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) if (!array)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) * The screen is divided in pages, each having a height of 8
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) * pixels, and the width of the screen. When sending a byte of
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) * data to the controller, it gives the 8 bits for the current
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) * column. I.e, the first byte are the 8 bits of the first
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) * column, then the 8 bits for the second column, etc.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) * Representation of the screen, assuming it is 5 bits
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) * wide. Each letter-number combination is a bit that controls
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) * one pixel.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) * A0 A1 A2 A3 A4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) * B0 B1 B2 B3 B4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) * C0 C1 C2 C3 C4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) * D0 D1 D2 D3 D4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) * E0 E1 E2 E3 E4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) * F0 F1 F2 F3 F4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) * G0 G1 G2 G3 G4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) * H0 H1 H2 H3 H4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) * If you want to update this screen, you need to send 5 bytes:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) * (1) A0 B0 C0 D0 E0 F0 G0 H0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) * (2) A1 B1 C1 D1 E1 F1 G1 H1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) * (3) A2 B2 C2 D2 E2 F2 G2 H2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) * (4) A3 B3 C3 D3 E3 F3 G3 H3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) * (5) A4 B4 C4 D4 E4 F4 G4 H4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) for (i = 0; i < pages; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) for (j = 0; j < par->width; j++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) int m = 8;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) u32 array_idx = i * par->width + j;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) array->data[array_idx] = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) /* Last page may be partial */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) if (i + 1 == pages && par->height % 8)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) m = par->height % 8;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) for (k = 0; k < m; k++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) u8 byte = vmem[(8 * i + k) * line_length +
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) j / 8];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) u8 bit = (byte >> (j % 8)) & 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) array->data[array_idx] |= bit << k;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) ssd1307fb_write_array(par->client, array, par->width * pages);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) kfree(array);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) static ssize_t ssd1307fb_write(struct fb_info *info, const char __user *buf,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) size_t count, loff_t *ppos)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) struct ssd1307fb_par *par = info->par;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) unsigned long total_size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) unsigned long p = *ppos;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) void *dst;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) total_size = info->fix.smem_len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) if (p > total_size)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) if (count + p > total_size)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) count = total_size - p;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) if (!count)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) dst = info->screen_buffer + p;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) if (copy_from_user(dst, buf, count))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) ssd1307fb_update_display(par);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) *ppos += count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) return count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) static int ssd1307fb_blank(int blank_mode, struct fb_info *info)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) struct ssd1307fb_par *par = info->par;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) if (blank_mode != FB_BLANK_UNBLANK)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) return ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_OFF);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) return ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) static void ssd1307fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) struct ssd1307fb_par *par = info->par;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) sys_fillrect(info, rect);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) ssd1307fb_update_display(par);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) static void ssd1307fb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) struct ssd1307fb_par *par = info->par;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) sys_copyarea(info, area);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) ssd1307fb_update_display(par);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) static void ssd1307fb_imageblit(struct fb_info *info, const struct fb_image *image)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) struct ssd1307fb_par *par = info->par;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) sys_imageblit(info, image);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) ssd1307fb_update_display(par);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) static const struct fb_ops ssd1307fb_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) .fb_read = fb_sys_read,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) .fb_write = ssd1307fb_write,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) .fb_blank = ssd1307fb_blank,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) .fb_fillrect = ssd1307fb_fillrect,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) .fb_copyarea = ssd1307fb_copyarea,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) .fb_imageblit = ssd1307fb_imageblit,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) static void ssd1307fb_deferred_io(struct fb_info *info,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) struct list_head *pagelist)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) ssd1307fb_update_display(info->par);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) static int ssd1307fb_init(struct ssd1307fb_par *par)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) struct pwm_state pwmstate;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) u32 precharge, dclk, com_invdir, compins;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) if (par->device_info->need_pwm) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) par->pwm = pwm_get(&par->client->dev, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) if (IS_ERR(par->pwm)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) dev_err(&par->client->dev, "Could not get PWM from device tree!\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) return PTR_ERR(par->pwm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) pwm_init_state(par->pwm, &pwmstate);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) pwm_set_relative_duty_cycle(&pwmstate, 50, 100);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) pwm_apply_state(par->pwm, &pwmstate);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) /* Enable the PWM */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) pwm_enable(par->pwm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) dev_dbg(&par->client->dev, "Using PWM%d with a %lluns period.\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) par->pwm->pwm, pwm_get_period(par->pwm));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) /* Set initial contrast */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) ret = ssd1307fb_write_cmd(par->client, par->contrast);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) /* Set segment re-map */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) if (par->seg_remap) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) /* Set COM direction */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) com_invdir = 0xc0 | par->com_invdir << 3;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) ret = ssd1307fb_write_cmd(par->client, com_invdir);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) /* Set multiplex ratio value */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_MULTIPLEX_RATIO);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) ret = ssd1307fb_write_cmd(par->client, par->height - 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) /* set display offset value */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_DISPLAY_OFFSET);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) ret = ssd1307fb_write_cmd(par->client, par->com_offset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) /* Set clock frequency */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_CLOCK_FREQ);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) dclk = ((par->dclk_div - 1) & 0xf) | (par->dclk_frq & 0xf) << 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) ret = ssd1307fb_write_cmd(par->client, dclk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) /* Set Set Area Color Mode ON/OFF & Low Power Display Mode */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) if (par->area_color_enable || par->low_power) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) u32 mode;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) ret = ssd1307fb_write_cmd(par->client,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) SSD1307FB_SET_AREA_COLOR_MODE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) mode = (par->area_color_enable ? 0x30 : 0) |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) (par->low_power ? 5 : 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) ret = ssd1307fb_write_cmd(par->client, mode);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) /* Set precharge period in number of ticks from the internal clock */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PRECHARGE_PERIOD);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) precharge = (par->prechargep1 & 0xf) | (par->prechargep2 & 0xf) << 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) ret = ssd1307fb_write_cmd(par->client, precharge);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) /* Set COM pins configuration */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COM_PINS_CONFIG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) compins = 0x02 | !par->com_seq << 4 | par->com_lrremap << 5;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) ret = ssd1307fb_write_cmd(par->client, compins);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) /* Set VCOMH */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_VCOMH);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) ret = ssd1307fb_write_cmd(par->client, par->vcomh);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) /* Turn on the DC-DC Charge Pump */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CHARGE_PUMP);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) ret = ssd1307fb_write_cmd(par->client,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) BIT(4) | (par->device_info->need_chargepump ? BIT(2) : 0));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) /* Set lookup table */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) if (par->lookup_table_set) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429) ret = ssd1307fb_write_cmd(par->client,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430) SSD1307FB_SET_LOOKUP_TABLE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434) for (i = 0; i < ARRAY_SIZE(par->lookup_table); ++i) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) u8 val = par->lookup_table[i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) if (val < 31 || val > 63)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) dev_warn(&par->client->dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) "lookup table index %d value out of range 31 <= %d <= 63\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440) i, val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441) ret = ssd1307fb_write_cmd(par->client, val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447) /* Switch to horizontal addressing mode */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_ADDRESS_MODE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452) ret = ssd1307fb_write_cmd(par->client,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457) /* Set column range */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458) ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COL_RANGE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 462) ret = ssd1307fb_write_cmd(par->client, par->col_offset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 463) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 466) ret = ssd1307fb_write_cmd(par->client, par->col_offset + par->width - 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 467) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 468) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 469)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 470) /* Set page range */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 471) ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PAGE_RANGE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 472) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 473) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 474)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 475) ret = ssd1307fb_write_cmd(par->client, par->page_offset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 476) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 477) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 478)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 479) ret = ssd1307fb_write_cmd(par->client,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 480) par->page_offset +
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 481) DIV_ROUND_UP(par->height, 8) - 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 482) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 483) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 484)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 485) /* Clear the screen */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 486) ssd1307fb_update_display(par);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 487)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 488) /* Turn on the display */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 489) ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 490) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 491) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 492)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 493) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 494) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 495)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 496) static int ssd1307fb_update_bl(struct backlight_device *bdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 497) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 498) struct ssd1307fb_par *par = bl_get_data(bdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 499) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 500) int brightness = bdev->props.brightness;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 501)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 502) par->contrast = brightness;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 503)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 504) ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 505) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 506) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 507) ret = ssd1307fb_write_cmd(par->client, par->contrast);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 508) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 509) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 510) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 511) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 512)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 513) static int ssd1307fb_get_brightness(struct backlight_device *bdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 514) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 515) struct ssd1307fb_par *par = bl_get_data(bdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 516)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 517) return par->contrast;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 518) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 519)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 520) static int ssd1307fb_check_fb(struct backlight_device *bdev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 521) struct fb_info *info)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 522) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 523) return (info->bl_dev == bdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 524) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 525)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 526) static const struct backlight_ops ssd1307fb_bl_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 527) .options = BL_CORE_SUSPENDRESUME,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 528) .update_status = ssd1307fb_update_bl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 529) .get_brightness = ssd1307fb_get_brightness,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 530) .check_fb = ssd1307fb_check_fb,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 531) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 532)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 533) static struct ssd1307fb_deviceinfo ssd1307fb_ssd1305_deviceinfo = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 534) .default_vcomh = 0x34,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 535) .default_dclk_div = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 536) .default_dclk_frq = 7,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 537) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 538)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 539) static struct ssd1307fb_deviceinfo ssd1307fb_ssd1306_deviceinfo = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 540) .default_vcomh = 0x20,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 541) .default_dclk_div = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 542) .default_dclk_frq = 8,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 543) .need_chargepump = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 544) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 545)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 546) static struct ssd1307fb_deviceinfo ssd1307fb_ssd1307_deviceinfo = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 547) .default_vcomh = 0x20,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 548) .default_dclk_div = 2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 549) .default_dclk_frq = 12,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 550) .need_pwm = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 551) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 552)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 553) static struct ssd1307fb_deviceinfo ssd1307fb_ssd1309_deviceinfo = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 554) .default_vcomh = 0x34,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 555) .default_dclk_div = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 556) .default_dclk_frq = 10,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 557) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 558)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 559) static const struct of_device_id ssd1307fb_of_match[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 560) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 561) .compatible = "solomon,ssd1305fb-i2c",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 562) .data = (void *)&ssd1307fb_ssd1305_deviceinfo,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 563) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 564) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 565) .compatible = "solomon,ssd1306fb-i2c",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 566) .data = (void *)&ssd1307fb_ssd1306_deviceinfo,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 567) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 568) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 569) .compatible = "solomon,ssd1307fb-i2c",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 570) .data = (void *)&ssd1307fb_ssd1307_deviceinfo,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 571) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 572) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 573) .compatible = "solomon,ssd1309fb-i2c",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 574) .data = (void *)&ssd1307fb_ssd1309_deviceinfo,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 575) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 576) {},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 577) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 578) MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 579)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 580) static int ssd1307fb_probe(struct i2c_client *client)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 581) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 582) struct device *dev = &client->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 583) struct backlight_device *bl;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 584) char bl_name[12];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 585) struct fb_info *info;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 586) struct fb_deferred_io *ssd1307fb_defio;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 587) u32 vmem_size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 588) struct ssd1307fb_par *par;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 589) void *vmem;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 590) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 591)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 592) info = framebuffer_alloc(sizeof(struct ssd1307fb_par), dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 593) if (!info)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 594) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 595)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 596) par = info->par;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 597) par->info = info;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 598) par->client = client;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 599)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 600) par->device_info = device_get_match_data(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 601)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 602) par->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 603) if (IS_ERR(par->reset)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 604) dev_err(dev, "failed to get reset gpio: %ld\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 605) PTR_ERR(par->reset));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 606) ret = PTR_ERR(par->reset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 607) goto fb_alloc_error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 608) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 609)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 610) par->vbat_reg = devm_regulator_get_optional(dev, "vbat");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 611) if (IS_ERR(par->vbat_reg)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 612) ret = PTR_ERR(par->vbat_reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 613) if (ret == -ENODEV) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 614) par->vbat_reg = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 615) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 616) dev_err(dev, "failed to get VBAT regulator: %d\n", ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 617) goto fb_alloc_error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 618) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 619) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 620)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 621) if (device_property_read_u32(dev, "solomon,width", &par->width))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 622) par->width = 96;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 623)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 624) if (device_property_read_u32(dev, "solomon,height", &par->height))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 625) par->height = 16;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 626)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 627) if (device_property_read_u32(dev, "solomon,page-offset", &par->page_offset))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 628) par->page_offset = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 629)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 630) if (device_property_read_u32(dev, "solomon,col-offset", &par->col_offset))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 631) par->col_offset = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 632)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 633) if (device_property_read_u32(dev, "solomon,com-offset", &par->com_offset))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 634) par->com_offset = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 635)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 636) if (device_property_read_u32(dev, "solomon,prechargep1", &par->prechargep1))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 637) par->prechargep1 = 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 638)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 639) if (device_property_read_u32(dev, "solomon,prechargep2", &par->prechargep2))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 640) par->prechargep2 = 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 641)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 642) if (!device_property_read_u8_array(dev, "solomon,lookup-table",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 643) par->lookup_table,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 644) ARRAY_SIZE(par->lookup_table)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 645) par->lookup_table_set = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 646)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 647) par->seg_remap = !device_property_read_bool(dev, "solomon,segment-no-remap");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 648) par->com_seq = device_property_read_bool(dev, "solomon,com-seq");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 649) par->com_lrremap = device_property_read_bool(dev, "solomon,com-lrremap");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 650) par->com_invdir = device_property_read_bool(dev, "solomon,com-invdir");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 651) par->area_color_enable =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 652) device_property_read_bool(dev, "solomon,area-color-enable");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 653) par->low_power = device_property_read_bool(dev, "solomon,low-power");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 654)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 655) par->contrast = 127;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 656) par->vcomh = par->device_info->default_vcomh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 657)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 658) /* Setup display timing */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 659) if (device_property_read_u32(dev, "solomon,dclk-div", &par->dclk_div))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 660) par->dclk_div = par->device_info->default_dclk_div;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 661) if (device_property_read_u32(dev, "solomon,dclk-frq", &par->dclk_frq))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 662) par->dclk_frq = par->device_info->default_dclk_frq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 663)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 664) vmem_size = DIV_ROUND_UP(par->width, 8) * par->height;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 665)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 666) vmem = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 667) get_order(vmem_size));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 668) if (!vmem) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 669) dev_err(dev, "Couldn't allocate graphical memory.\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 670) ret = -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 671) goto fb_alloc_error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 672) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 673)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 674) ssd1307fb_defio = devm_kzalloc(dev, sizeof(*ssd1307fb_defio),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 675) GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 676) if (!ssd1307fb_defio) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 677) dev_err(dev, "Couldn't allocate deferred io.\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 678) ret = -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 679) goto fb_alloc_error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 680) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 681)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 682) ssd1307fb_defio->delay = HZ / refreshrate;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 683) ssd1307fb_defio->deferred_io = ssd1307fb_deferred_io;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 684)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 685) info->fbops = &ssd1307fb_ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 686) info->fix = ssd1307fb_fix;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 687) info->fix.line_length = DIV_ROUND_UP(par->width, 8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 688) info->fbdefio = ssd1307fb_defio;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 689)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 690) info->var = ssd1307fb_var;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 691) info->var.xres = par->width;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 692) info->var.xres_virtual = par->width;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 693) info->var.yres = par->height;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 694) info->var.yres_virtual = par->height;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 695)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 696) info->screen_buffer = vmem;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 697) info->fix.smem_start = __pa(vmem);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 698) info->fix.smem_len = vmem_size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 699)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 700) fb_deferred_io_init(info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 701)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 702) i2c_set_clientdata(client, info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 703)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 704) if (par->reset) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 705) /* Reset the screen */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 706) gpiod_set_value_cansleep(par->reset, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 707) udelay(4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 708) gpiod_set_value_cansleep(par->reset, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 709) udelay(4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 710) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 711)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 712) if (par->vbat_reg) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 713) ret = regulator_enable(par->vbat_reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 714) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 715) dev_err(dev, "failed to enable VBAT: %d\n", ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 716) goto reset_oled_error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 717) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 718) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 719)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 720) ret = ssd1307fb_init(par);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 721) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 722) goto regulator_enable_error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 723)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 724) ret = register_framebuffer(info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 725) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 726) dev_err(dev, "Couldn't register the framebuffer\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 727) goto panel_init_error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 728) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 729)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 730) snprintf(bl_name, sizeof(bl_name), "ssd1307fb%d", info->node);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 731) bl = backlight_device_register(bl_name, dev, par, &ssd1307fb_bl_ops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 732) NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 733) if (IS_ERR(bl)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 734) ret = PTR_ERR(bl);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 735) dev_err(dev, "unable to register backlight device: %d\n", ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 736) goto bl_init_error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 737) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 738)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 739) bl->props.brightness = par->contrast;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 740) bl->props.max_brightness = MAX_CONTRAST;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 741) info->bl_dev = bl;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 742)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 743) dev_info(dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 744)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 745) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 746)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 747) bl_init_error:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 748) unregister_framebuffer(info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 749) panel_init_error:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 750) if (par->device_info->need_pwm) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 751) pwm_disable(par->pwm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 752) pwm_put(par->pwm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 753) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 754) regulator_enable_error:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 755) if (par->vbat_reg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 756) regulator_disable(par->vbat_reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 757) reset_oled_error:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 758) fb_deferred_io_cleanup(info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 759) fb_alloc_error:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 760) framebuffer_release(info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 761) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 762) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 763)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 764) static int ssd1307fb_remove(struct i2c_client *client)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 765) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 766) struct fb_info *info = i2c_get_clientdata(client);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 767) struct ssd1307fb_par *par = info->par;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 768)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 769) ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_OFF);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 770)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 771) backlight_device_unregister(info->bl_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 772)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 773) unregister_framebuffer(info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 774) if (par->device_info->need_pwm) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 775) pwm_disable(par->pwm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 776) pwm_put(par->pwm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 777) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 778) if (par->vbat_reg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 779) regulator_disable(par->vbat_reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 780) fb_deferred_io_cleanup(info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 781) __free_pages(__va(info->fix.smem_start), get_order(info->fix.smem_len));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 782) framebuffer_release(info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 783)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 784) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 785) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 786)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 787) static const struct i2c_device_id ssd1307fb_i2c_id[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 788) { "ssd1305fb", 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 789) { "ssd1306fb", 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 790) { "ssd1307fb", 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 791) { "ssd1309fb", 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 792) { }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 793) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 794) MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 795)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 796) static struct i2c_driver ssd1307fb_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 797) .probe_new = ssd1307fb_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 798) .remove = ssd1307fb_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 799) .id_table = ssd1307fb_i2c_id,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 800) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 801) .name = "ssd1307fb",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 802) .of_match_table = ssd1307fb_of_match,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 803) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 804) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 805)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 806) module_i2c_driver(ssd1307fb_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 807)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 808) MODULE_DESCRIPTION("FB driver for the Solomon SSD1307 OLED controller");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 809) MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 810) MODULE_LICENSE("GPL");