diff options
Diffstat (limited to 'drivers/video/fbdev/ssd1307fb.c')
| -rw-r--r-- | drivers/video/fbdev/ssd1307fb.c | 463 |
1 files changed, 249 insertions, 214 deletions
diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c index 4061a20cfe24..83dd31fa1fab 100644 --- a/drivers/video/fbdev/ssd1307fb.c +++ b/drivers/video/fbdev/ssd1307fb.c @@ -1,9 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Driver for the Solomon SSD1307 OLED controller * * Copyright 2012 Free Electrons - * - * Licensed under the GPLv2 or later. */ #include <linux/backlight.h> @@ -13,8 +12,7 @@ #include <linux/i2c.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/of_device.h> -#include <linux/of_gpio.h> +#include <linux/property.h> #include <linux/pwm.h> #include <linux/uaccess.h> #include <linux/regulator/consumer.h> @@ -29,6 +27,7 @@ #define SSD1307FB_SET_COL_RANGE 0x21 #define SSD1307FB_SET_PAGE_RANGE 0x22 #define SSD1307FB_CONTRAST 0x81 +#define SSD1307FB_SET_LOOKUP_TABLE 0x91 #define SSD1307FB_CHARGE_PUMP 0x8d #define SSD1307FB_SEG_REMAP_ON 0xa1 #define SSD1307FB_DISPLAY_OFF 0xae @@ -37,6 +36,7 @@ #define SSD1307FB_START_PAGE_ADDRESS 0xb0 #define SSD1307FB_SET_DISPLAY_OFFSET 0xd3 #define SSD1307FB_SET_CLOCK_FREQ 0xd5 +#define SSD1307FB_SET_AREA_COLOR_MODE 0xd8 #define SSD1307FB_SET_PRECHARGE_PERIOD 0xd9 #define SSD1307FB_SET_COM_PINS_CONFIG 0xda #define SSD1307FB_SET_VCOMH 0xdb @@ -48,21 +48,23 @@ static u_int refreshrate = REFRESHRATE; module_param(refreshrate, uint, 0); -struct ssd1307fb_par; - struct ssd1307fb_deviceinfo { u32 default_vcomh; u32 default_dclk_div; u32 default_dclk_frq; - int need_pwm; - int need_chargepump; + bool need_pwm; + bool need_chargepump; }; struct ssd1307fb_par { - u32 com_invdir; - u32 com_lrremap; + unsigned area_color_enable : 1; + unsigned com_invdir : 1; + unsigned com_lrremap : 1; + unsigned com_seq : 1; + unsigned lookup_table_set : 1; + unsigned low_power : 1; + unsigned seg_remap : 1; u32 com_offset; - u32 com_seq; u32 contrast; u32 dclk_div; u32 dclk_frq; @@ -70,21 +72,26 @@ struct ssd1307fb_par { struct i2c_client *client; u32 height; struct fb_info *info; + u8 lookup_table[4]; u32 page_offset; + u32 col_offset; u32 prechargep1; u32 prechargep2; struct pwm_device *pwm; - u32 pwm_period; struct gpio_desc *reset; struct regulator *vbat_reg; - u32 seg_remap; u32 vcomh; u32 width; + /* Cached address ranges */ + u8 col_start; + u8 col_end; + u8 page_start; + u8 page_end; }; struct ssd1307fb_array { u8 type; - u8 data[0]; + u8 data[]; }; static const struct fb_fix_screeninfo ssd1307fb_fix = { @@ -99,6 +106,9 @@ static const struct fb_fix_screeninfo ssd1307fb_fix = { static const struct fb_var_screeninfo ssd1307fb_var = { .bits_per_pixel = 1, + .red = { .length = 1 }, + .green = { .length = 1 }, + .blue = { .length = 1 }, }; static struct ssd1307fb_array *ssd1307fb_alloc_array(u32 len, u8 type) @@ -147,16 +157,72 @@ static inline int ssd1307fb_write_cmd(struct i2c_client *client, u8 cmd) return ret; } -static void ssd1307fb_update_display(struct ssd1307fb_par *par) +static int ssd1307fb_set_col_range(struct ssd1307fb_par *par, u8 col_start, + u8 cols) +{ + u8 col_end = col_start + cols - 1; + int ret; + + if (col_start == par->col_start && col_end == par->col_end) + return 0; + + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COL_RANGE); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, col_start); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, col_end); + if (ret < 0) + return ret; + + par->col_start = col_start; + par->col_end = col_end; + return 0; +} + +static int ssd1307fb_set_page_range(struct ssd1307fb_par *par, u8 page_start, + u8 pages) +{ + u8 page_end = page_start + pages - 1; + int ret; + + if (page_start == par->page_start && page_end == par->page_end) + return 0; + + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PAGE_RANGE); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, page_start); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, page_end); + if (ret < 0) + return ret; + + par->page_start = page_start; + par->page_end = page_end; + return 0; +} + +static int ssd1307fb_update_rect(struct ssd1307fb_par *par, unsigned int x, + unsigned int y, unsigned int width, + unsigned int height) { struct ssd1307fb_array *array; - u8 *vmem = par->info->screen_base; - int i, j, k; + u8 *vmem = par->info->screen_buffer; + unsigned int line_length = par->info->fix.line_length; + unsigned int pages = DIV_ROUND_UP(y % 8 + height, 8); + u32 array_idx = 0; + int ret, i, j, k; - array = ssd1307fb_alloc_array(par->width * par->height / 8, - SSD1307FB_DATA); + array = ssd1307fb_alloc_array(width * pages, SSD1307FB_DATA); if (!array) - return; + return -ENOMEM; /* * The screen is divided in pages, each having a height of 8 @@ -187,55 +253,43 @@ static void ssd1307fb_update_display(struct ssd1307fb_par *par) * (5) A4 B4 C4 D4 E4 F4 G4 H4 */ - for (i = 0; i < (par->height / 8); i++) { - for (j = 0; j < par->width; j++) { - u32 array_idx = i * par->width + j; - array->data[array_idx] = 0; - for (k = 0; k < 8; k++) { - u32 page_length = par->width * i; - u32 index = page_length + (par->width * k + j) / 8; - u8 byte = *(vmem + index); - u8 bit = byte & (1 << (j % 8)); - bit = bit >> (j % 8); - array->data[array_idx] |= bit << k; + ret = ssd1307fb_set_col_range(par, par->col_offset + x, width); + if (ret < 0) + goto out_free; + + ret = ssd1307fb_set_page_range(par, par->page_offset + y / 8, pages); + if (ret < 0) + goto out_free; + + for (i = y / 8; i < y / 8 + pages; i++) { + int m = 8; + + /* Last page may be partial */ + if (8 * (i + 1) > par->height) + m = par->height % 8; + for (j = x; j < x + width; j++) { + u8 data = 0; + + for (k = 0; k < m; k++) { + u8 byte = vmem[(8 * i + k) * line_length + + j / 8]; + u8 bit = (byte >> (j % 8)) & 1; + data |= bit << k; } + array->data[array_idx++] = data; } } - ssd1307fb_write_array(par->client, array, par->width * par->height / 8); + ret = ssd1307fb_write_array(par->client, array, width * pages); + +out_free: kfree(array); + return ret; } - -static ssize_t ssd1307fb_write(struct fb_info *info, const char __user *buf, - size_t count, loff_t *ppos) +static int ssd1307fb_update_display(struct ssd1307fb_par *par) { - struct ssd1307fb_par *par = info->par; - unsigned long total_size; - unsigned long p = *ppos; - u8 __iomem *dst; - - total_size = info->fix.smem_len; - - if (p > total_size) - return -EINVAL; - - if (count + p > total_size) - count = total_size - p; - - if (!count) - return -EINVAL; - - dst = (void __force *) (info->screen_base + p); - - if (copy_from_user(dst, buf, count)) - return -EFAULT; - - ssd1307fb_update_display(par); - - *ppos += count; - - return count; + return ssd1307fb_update_rect(par, 0, 0, par->width, par->height); } static int ssd1307fb_blank(int blank_mode, struct fb_info *info) @@ -248,48 +302,41 @@ static int ssd1307fb_blank(int blank_mode, struct fb_info *info) return ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON); } -static void ssd1307fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) +static void ssd1307fb_defio_damage_range(struct fb_info *info, off_t off, size_t len) { struct ssd1307fb_par *par = info->par; - sys_fillrect(info, rect); - ssd1307fb_update_display(par); -} -static void ssd1307fb_copyarea(struct fb_info *info, const struct fb_copyarea *area) -{ - struct ssd1307fb_par *par = info->par; - sys_copyarea(info, area); ssd1307fb_update_display(par); } -static void ssd1307fb_imageblit(struct fb_info *info, const struct fb_image *image) +static void ssd1307fb_defio_damage_area(struct fb_info *info, u32 x, u32 y, + u32 width, u32 height) { struct ssd1307fb_par *par = info->par; - sys_imageblit(info, image); - ssd1307fb_update_display(par); + + ssd1307fb_update_rect(par, x, y, width, height); } -static struct fb_ops ssd1307fb_ops = { +FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(ssd1307fb, + ssd1307fb_defio_damage_range, + ssd1307fb_defio_damage_area) + +static const struct fb_ops ssd1307fb_ops = { .owner = THIS_MODULE, - .fb_read = fb_sys_read, - .fb_write = ssd1307fb_write, + FB_DEFAULT_DEFERRED_OPS(ssd1307fb), .fb_blank = ssd1307fb_blank, - .fb_fillrect = ssd1307fb_fillrect, - .fb_copyarea = ssd1307fb_copyarea, - .fb_imageblit = ssd1307fb_imageblit, }; -static void ssd1307fb_deferred_io(struct fb_info *info, - struct list_head *pagelist) +static void ssd1307fb_deferred_io(struct fb_info *info, struct list_head *pagereflist) { ssd1307fb_update_display(info->par); } static int ssd1307fb_init(struct ssd1307fb_par *par) { + struct pwm_state pwmstate; int ret; u32 precharge, dclk, com_invdir, compins; - struct pwm_args pargs; if (par->device_info->need_pwm) { par->pwm = pwm_get(&par->client->dev, NULL); @@ -298,22 +345,16 @@ static int ssd1307fb_init(struct ssd1307fb_par *par) return PTR_ERR(par->pwm); } - /* - * FIXME: pwm_apply_args() should be removed when switching to - * the atomic PWM API. - */ - pwm_apply_args(par->pwm); + pwm_init_state(par->pwm, &pwmstate); + pwm_set_relative_duty_cycle(&pwmstate, 50, 100); + pwm_apply_might_sleep(par->pwm, &pwmstate); - pwm_get_args(par->pwm, &pargs); - - par->pwm_period = pargs.period; /* Enable the PWM */ - pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period); pwm_enable(par->pwm); - dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n", - par->pwm->pwm, par->pwm_period); - }; + dev_dbg(&par->client->dev, "Using PWM %s with a %lluns period.\n", + par->pwm->label, pwm_get_period(par->pwm)); + } /* Set initial contrast */ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST); @@ -329,10 +370,10 @@ static int ssd1307fb_init(struct ssd1307fb_par *par) ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON); if (ret < 0) return ret; - }; + } /* Set COM direction */ - com_invdir = 0xc0 | (par->com_invdir & 0x1) << 3; + com_invdir = 0xc0 | par->com_invdir << 3; ret = ssd1307fb_write_cmd(par->client, com_invdir); if (ret < 0) return ret; @@ -365,6 +406,22 @@ static int ssd1307fb_init(struct ssd1307fb_par *par) if (ret < 0) return ret; + /* Set Area Color Mode ON/OFF & Low Power Display Mode */ + if (par->area_color_enable || par->low_power) { + u32 mode; + + ret = ssd1307fb_write_cmd(par->client, + SSD1307FB_SET_AREA_COLOR_MODE); + if (ret < 0) + return ret; + + mode = (par->area_color_enable ? 0x30 : 0) | + (par->low_power ? 5 : 0); + ret = ssd1307fb_write_cmd(par->client, mode); + if (ret < 0) + return ret; + } + /* Set precharge period in number of ticks from the internal clock */ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PRECHARGE_PERIOD); if (ret < 0) @@ -380,8 +437,7 @@ static int ssd1307fb_init(struct ssd1307fb_par *par) if (ret < 0) return ret; - compins = 0x02 | !(par->com_seq & 0x1) << 4 - | (par->com_lrremap & 0x1) << 5; + compins = 0x02 | !par->com_seq << 4 | par->com_lrremap << 5; ret = ssd1307fb_write_cmd(par->client, compins); if (ret < 0) return ret; @@ -405,45 +461,42 @@ static int ssd1307fb_init(struct ssd1307fb_par *par) if (ret < 0) return ret; - /* Switch to horizontal addressing mode */ - ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_ADDRESS_MODE); - if (ret < 0) - return ret; + /* Set lookup table */ + if (par->lookup_table_set) { + int i; - ret = ssd1307fb_write_cmd(par->client, - SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL); - if (ret < 0) - return ret; - - /* Set column range */ - ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COL_RANGE); - if (ret < 0) - return ret; - - ret = ssd1307fb_write_cmd(par->client, 0x0); - if (ret < 0) - return ret; + ret = ssd1307fb_write_cmd(par->client, + SSD1307FB_SET_LOOKUP_TABLE); + if (ret < 0) + return ret; - ret = ssd1307fb_write_cmd(par->client, par->width - 1); - if (ret < 0) - return ret; + for (i = 0; i < ARRAY_SIZE(par->lookup_table); ++i) { + u8 val = par->lookup_table[i]; - /* Set page range */ - ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PAGE_RANGE); - if (ret < 0) - return ret; + if (val < 31 || val > 63) + dev_warn(&par->client->dev, + "lookup table index %d value out of range 31 <= %d <= 63\n", + i, val); + ret = ssd1307fb_write_cmd(par->client, val); + if (ret < 0) + return ret; + } + } - ret = ssd1307fb_write_cmd(par->client, 0x0); + /* Switch to horizontal addressing mode */ + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_ADDRESS_MODE); if (ret < 0) return ret; ret = ssd1307fb_write_cmd(par->client, - par->page_offset + (par->height / 8) - 1); + SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL); if (ret < 0) return ret; /* Clear the screen */ - ssd1307fb_update_display(par); + ret = ssd1307fb_update_display(par); + if (ret < 0) + return ret; /* Turn on the display */ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON); @@ -477,17 +530,10 @@ static int ssd1307fb_get_brightness(struct backlight_device *bdev) return par->contrast; } -static int ssd1307fb_check_fb(struct backlight_device *bdev, - struct fb_info *info) -{ - return (info->bl_dev == bdev); -} - static const struct backlight_ops ssd1307fb_bl_ops = { .options = BL_CORE_SUSPENDRESUME, .update_status = ssd1307fb_update_bl, .get_brightness = ssd1307fb_get_brightness, - .check_fb = ssd1307fb_check_fb, }; static struct ssd1307fb_deviceinfo ssd1307fb_ssd1305_deviceinfo = { @@ -537,103 +583,104 @@ static const struct of_device_id ssd1307fb_of_match[] = { }; MODULE_DEVICE_TABLE(of, ssd1307fb_of_match); -static int ssd1307fb_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ssd1307fb_probe(struct i2c_client *client) { + struct device *dev = &client->dev; struct backlight_device *bl; - char bl_name[12]; struct fb_info *info; - struct device_node *node = client->dev.of_node; struct fb_deferred_io *ssd1307fb_defio; u32 vmem_size; struct ssd1307fb_par *par; - u8 *vmem; + void *vmem; int ret; - if (!node) { - dev_err(&client->dev, "No device tree data found!\n"); - return -EINVAL; - } - - info = framebuffer_alloc(sizeof(struct ssd1307fb_par), &client->dev); - if (!info) { - dev_err(&client->dev, "Couldn't allocate framebuffer.\n"); + info = framebuffer_alloc(sizeof(struct ssd1307fb_par), dev); + if (!info) return -ENOMEM; - } par = info->par; par->info = info; par->client = client; - par->device_info = of_device_get_match_data(&client->dev); + par->device_info = device_get_match_data(dev); - par->reset = devm_gpiod_get_optional(&client->dev, "reset", - GPIOD_OUT_LOW); + par->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(par->reset)) { - dev_err(&client->dev, "failed to get reset gpio: %ld\n", - PTR_ERR(par->reset)); - ret = PTR_ERR(par->reset); + ret = dev_err_probe(dev, PTR_ERR(par->reset), + "failed to get reset gpio\n"); goto fb_alloc_error; } - par->vbat_reg = devm_regulator_get_optional(&client->dev, "vbat"); + par->vbat_reg = devm_regulator_get_optional(dev, "vbat"); if (IS_ERR(par->vbat_reg)) { ret = PTR_ERR(par->vbat_reg); if (ret == -ENODEV) { par->vbat_reg = NULL; } else { - dev_err(&client->dev, "failed to get VBAT regulator: %d\n", - ret); + dev_err_probe(dev, ret, "failed to get VBAT regulator\n"); goto fb_alloc_error; } } - if (of_property_read_u32(node, "solomon,width", &par->width)) + if (device_property_read_u32(dev, "solomon,width", &par->width)) par->width = 96; - if (of_property_read_u32(node, "solomon,height", &par->height)) + if (device_property_read_u32(dev, "solomon,height", &par->height)) par->height = 16; - if (of_property_read_u32(node, "solomon,page-offset", &par->page_offset)) + if (device_property_read_u32(dev, "solomon,page-offset", &par->page_offset)) par->page_offset = 1; - if (of_property_read_u32(node, "solomon,com-offset", &par->com_offset)) + if (device_property_read_u32(dev, "solomon,col-offset", &par->col_offset)) + par->col_offset = 0; + + if (device_property_read_u32(dev, "solomon,com-offset", &par->com_offset)) par->com_offset = 0; - if (of_property_read_u32(node, "solomon,prechargep1", &par->prechargep1)) + if (device_property_read_u32(dev, "solomon,prechargep1", &par->prechargep1)) par->prechargep1 = 2; - if (of_property_read_u32(node, "solomon,prechargep2", &par->prechargep2)) + if (device_property_read_u32(dev, "solomon,prechargep2", &par->prechargep2)) par->prechargep2 = 2; - par->seg_remap = !of_property_read_bool(node, "solomon,segment-no-remap"); - par->com_seq = of_property_read_bool(node, "solomon,com-seq"); - par->com_lrremap = of_property_read_bool(node, "solomon,com-lrremap"); - par->com_invdir = of_property_read_bool(node, "solomon,com-invdir"); + if (!device_property_read_u8_array(dev, "solomon,lookup-table", + par->lookup_table, + ARRAY_SIZE(par->lookup_table))) + par->lookup_table_set = 1; + + par->seg_remap = !device_property_read_bool(dev, "solomon,segment-no-remap"); + par->com_seq = device_property_read_bool(dev, "solomon,com-seq"); + par->com_lrremap = device_property_read_bool(dev, "solomon,com-lrremap"); + par->com_invdir = device_property_read_bool(dev, "solomon,com-invdir"); + par->area_color_enable = + device_property_read_bool(dev, "solomon,area-color-enable"); + par->low_power = device_property_read_bool(dev, "solomon,low-power"); par->contrast = 127; par->vcomh = par->device_info->default_vcomh; /* Setup display timing */ - par->dclk_div = par->device_info->default_dclk_div; - par->dclk_frq = par->device_info->default_dclk_frq; + if (device_property_read_u32(dev, "solomon,dclk-div", &par->dclk_div)) + par->dclk_div = par->device_info->default_dclk_div; + if (device_property_read_u32(dev, "solomon,dclk-frq", &par->dclk_frq)) + par->dclk_frq = par->device_info->default_dclk_frq; - vmem_size = par->width * par->height / 8; + vmem_size = DIV_ROUND_UP(par->width, 8) * par->height; vmem = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, get_order(vmem_size)); if (!vmem) { - dev_err(&client->dev, "Couldn't allocate graphical memory.\n"); + dev_err(dev, "Couldn't allocate graphical memory.\n"); ret = -ENOMEM; goto fb_alloc_error; } - ssd1307fb_defio = devm_kzalloc(&client->dev, sizeof(*ssd1307fb_defio), + ssd1307fb_defio = devm_kzalloc(dev, sizeof(*ssd1307fb_defio), GFP_KERNEL); if (!ssd1307fb_defio) { - dev_err(&client->dev, "Couldn't allocate deferred io.\n"); + dev_err(dev, "Couldn't allocate deferred io.\n"); ret = -ENOMEM; - goto fb_alloc_error; + goto fb_defio_error; } ssd1307fb_defio->delay = HZ / refreshrate; @@ -641,7 +688,7 @@ static int ssd1307fb_probe(struct i2c_client *client, info->fbops = &ssd1307fb_ops; info->fix = ssd1307fb_fix; - info->fix.line_length = par->width / 8; + info->fix.line_length = DIV_ROUND_UP(par->width, 8); info->fbdefio = ssd1307fb_defio; info->var = ssd1307fb_var; @@ -650,14 +697,7 @@ static int ssd1307fb_probe(struct i2c_client *client, info->var.yres = par->height; info->var.yres_virtual = par->height; - info->var.red.length = 1; - info->var.red.offset = 0; - info->var.green.length = 1; - info->var.green.offset = 0; - info->var.blue.length = 1; - info->var.blue.offset = 0; - - info->screen_base = (u8 __force __iomem *)vmem; + info->screen_buffer = vmem; info->fix.smem_start = __pa(vmem); info->fix.smem_len = vmem_size; @@ -667,17 +707,16 @@ static int ssd1307fb_probe(struct i2c_client *client, if (par->reset) { /* Reset the screen */ - gpiod_set_value_cansleep(par->reset, 0); - udelay(4); gpiod_set_value_cansleep(par->reset, 1); udelay(4); + gpiod_set_value_cansleep(par->reset, 0); + udelay(4); } if (par->vbat_reg) { ret = regulator_enable(par->vbat_reg); if (ret) { - dev_err(&client->dev, "failed to enable VBAT: %d\n", - ret); + dev_err(dev, "failed to enable VBAT: %d\n", ret); goto reset_oled_error; } } @@ -686,48 +725,46 @@ static int ssd1307fb_probe(struct i2c_client *client, if (ret) goto regulator_enable_error; - ret = register_framebuffer(info); - if (ret) { - dev_err(&client->dev, "Couldn't register the framebuffer\n"); + bl = backlight_device_register("ssd1307fb-bl", dev, par, &ssd1307fb_bl_ops, + NULL); + if (IS_ERR(bl)) { + ret = PTR_ERR(bl); + dev_err(dev, "unable to register backlight device: %d\n", ret); goto panel_init_error; } + info->bl_dev = bl; - snprintf(bl_name, sizeof(bl_name), "ssd1307fb%d", info->node); - bl = backlight_device_register(bl_name, &client->dev, par, - &ssd1307fb_bl_ops, NULL); - if (IS_ERR(bl)) { - ret = PTR_ERR(bl); - dev_err(&client->dev, "unable to register backlight device: %d\n", - ret); - goto bl_init_error; + ret = register_framebuffer(info); + if (ret) { + dev_err(dev, "Couldn't register the framebuffer\n"); + goto fb_init_error; } bl->props.brightness = par->contrast; bl->props.max_brightness = MAX_CONTRAST; - info->bl_dev = bl; - dev_info(&client->dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size); + dev_info(dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size); return 0; -bl_init_error: - unregister_framebuffer(info); +fb_init_error: + backlight_device_unregister(bl); panel_init_error: - if (par->device_info->need_pwm) { - pwm_disable(par->pwm); - pwm_put(par->pwm); - }; + pwm_disable(par->pwm); + pwm_put(par->pwm); regulator_enable_error: if (par->vbat_reg) regulator_disable(par->vbat_reg); reset_oled_error: fb_deferred_io_cleanup(info); +fb_defio_error: + __free_pages(vmem, get_order(vmem_size)); fb_alloc_error: framebuffer_release(info); return ret; } -static int ssd1307fb_remove(struct i2c_client *client) +static void ssd1307fb_remove(struct i2c_client *client) { struct fb_info *info = i2c_get_clientdata(client); struct ssd1307fb_par *par = info->par; @@ -737,22 +774,20 @@ static int ssd1307fb_remove(struct i2c_client *client) backlight_device_unregister(info->bl_dev); unregister_framebuffer(info); - if (par->device_info->need_pwm) { - pwm_disable(par->pwm); - pwm_put(par->pwm); - }; + pwm_disable(par->pwm); + pwm_put(par->pwm); + if (par->vbat_reg) + regulator_disable(par->vbat_reg); fb_deferred_io_cleanup(info); __free_pages(__va(info->fix.smem_start), get_order(info->fix.smem_len)); framebuffer_release(info); - - return 0; } static const struct i2c_device_id ssd1307fb_i2c_id[] = { - { "ssd1305fb", 0 }, - { "ssd1306fb", 0 }, - { "ssd1307fb", 0 }, - { "ssd1309fb", 0 }, + { "ssd1305fb" }, + { "ssd1306fb" }, + { "ssd1307fb" }, + { "ssd1309fb" }, { } }; MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id); |
