On 17:44 Wed 06 Mar     , Maxime Ripard wrote:
> The Solomon SSD1306 OLED controller is very similar to the SSD1307,
> except for the fact that the power is given through an external PWM for
> the 1307, and while the 1306 can generate its own power without any PWM.
> 
> Signed-off-by: Maxime Ripard <[email protected]>
> ---
>  .../devicetree/bindings/video/ssd1307fb.txt        |   10 +-
>  drivers/video/ssd1307fb.c                          |  267 
> ++++++++++++++------
>  2 files changed, 203 insertions(+), 74 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/video/ssd1307fb.txt 
> b/Documentation/devicetree/bindings/video/ssd1307fb.txt
> index 3d0060c..7a12542 100644
> --- a/Documentation/devicetree/bindings/video/ssd1307fb.txt
> +++ b/Documentation/devicetree/bindings/video/ssd1307fb.txt
> @@ -1,13 +1,17 @@
>  * Solomon SSD1307 Framebuffer Driver
>  
>  Required properties:
> -  - compatible: Should be "solomon,ssd1307fb-<bus>". The only supported bus 
> for
> -    now is i2c.
> +  - compatible: Should be "solomon,<chip>fb-<bus>". The only supported bus 
> for
> +    now is i2c, and the supported chips are ssd1306 and ssd1307.
>    - reg: Should contain address of the controller on the I2C bus. Most likely
>           0x3c or 0x3d
>    - pwm: Should contain the pwm to use according to the OF device tree PWM
> -         specification [0]
> +         specification [0]. Only required for the ssd1307.
>    - reset-gpios: Should contain the GPIO used to reset the OLED display
> +  - solomon,height: Height in pixel of the screen driven by the controller
> +  - solomon,width: Width in pixel of the screen driven by the controller
> +  - solomon,page-offset: Offset of pages (band of 8 pixels) that the screen 
> is
> +    mapped to.
>  
>  Optional properties:
>    - reset-active-low: Is the reset gpio is active on physical low?
> diff --git a/drivers/video/ssd1307fb.c b/drivers/video/ssd1307fb.c
> index 395cb6a..95f76e2 100644
> --- a/drivers/video/ssd1307fb.c
> +++ b/drivers/video/ssd1307fb.c
> @@ -16,24 +16,39 @@
>  #include <linux/pwm.h>
>  #include <linux/delay.h>
>  
> -#define SSD1307FB_WIDTH                      96
> -#define SSD1307FB_HEIGHT             16
> -
>  #define SSD1307FB_DATA                       0x40
>  #define SSD1307FB_COMMAND            0x80
>  
>  #define SSD1307FB_CONTRAST           0x81
> +#define      SSD1307FB_CHARGE_PUMP           0x8d
>  #define SSD1307FB_SEG_REMAP_ON               0xa1
>  #define SSD1307FB_DISPLAY_OFF                0xae
> +#define SSD1307FB_SET_MULTIPLEX_RATIO        0xa8
>  #define SSD1307FB_DISPLAY_ON         0xaf
>  #define SSD1307FB_START_PAGE_ADDRESS 0xb0
> +#define SSD1307FB_SET_DISPLAY_OFFSET 0xd3
> +#define      SSD1307FB_SET_CLOCK_FREQ        0xd5
> +#define      SSD1307FB_SET_PRECHARGE_PERIOD  0xd9
> +#define      SSD1307FB_SET_COM_PINS_CONFIG   0xda
> +#define      SSD1307FB_SET_VCOMH             0xdb
> +
> +struct ssd1307fb_par;
> +
> +struct ssd1307fb_ops {
> +     int (*init)(struct ssd1307fb_par *);
> +     int (*remove)(struct ssd1307fb_par *);
> +};
>  
>  struct ssd1307fb_par {
>       struct i2c_client *client;
> +     u32 height;
>       struct fb_info *info;
> +     struct ssd1307fb_ops *ops;
> +     u32 page_offset;
>       struct pwm_device *pwm;
>       u32 pwm_period;
>       int reset;
> +     u32 width;
>  };
>  
>  static struct fb_fix_screeninfo ssd1307fb_fix = {
> @@ -43,15 +58,10 @@ static struct fb_fix_screeninfo ssd1307fb_fix = {
>       .xpanstep       = 0,
>       .ypanstep       = 0,
>       .ywrapstep      = 0,
> -     .line_length    = SSD1307FB_WIDTH / 8,
>       .accel          = FB_ACCEL_NONE,
>  };
>  
>  static struct fb_var_screeninfo ssd1307fb_var = {
> -     .xres           = SSD1307FB_WIDTH,
> -     .yres           = SSD1307FB_HEIGHT,
> -     .xres_virtual   = SSD1307FB_WIDTH,
> -     .yres_virtual   = SSD1307FB_HEIGHT,
>       .bits_per_pixel = 1,
>  };
>  
> @@ -134,16 +144,16 @@ static void ssd1307fb_update_display(struct 
> ssd1307fb_par *par)
>        *  (5) A4 B4 C4 D4 E4 F4 G4 H4
>        */
>  
> -     for (i = 0; i < (SSD1307FB_HEIGHT / 8); i++) {
> -             ssd1307fb_write_cmd(par->client, SSD1307FB_START_PAGE_ADDRESS + 
> (i + 1));
> +     for (i = 0; i < (par->height / 8); i++) {
> +             ssd1307fb_write_cmd(par->client, SSD1307FB_START_PAGE_ADDRESS + 
> i + par->page_offset);
>               ssd1307fb_write_cmd(par->client, 0x00);
>               ssd1307fb_write_cmd(par->client, 0x10);
>  
> -             for (j = 0; j < SSD1307FB_WIDTH; j++) {
> +             for (j = 0; j < par->width; j++) {
>                       u8 buf = 0;
>                       for (k = 0; k < 8; k++) {
> -                             u32 page_length = SSD1307FB_WIDTH * i;
> -                             u32 index = page_length + (SSD1307FB_WIDTH * k 
> + j) / 8;
> +                             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);
> @@ -227,16 +237,137 @@ static struct fb_deferred_io ssd1307fb_defio = {
>       .deferred_io    = ssd1307fb_deferred_io,
>  };
>  
> +static int ssd1307fb_ssd1307_init(struct ssd1307fb_par *par) {
> +     int ret;
> +
> +     par->pwm = pwm_get(&par->client->dev, NULL);
> +     if (IS_ERR(par->pwm)) {
> +             dev_err(&par->client->dev, "Could not get PWM from device 
> tree!\n");
> +             return PTR_ERR(par->pwm);
> +     }
> +
> +     par->pwm_period = pwm_get_period(par->pwm);
> +     /* 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);
> +
> +     /* Map column 127 of the OLED to segment 0 */
> +     ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
> +     if (ret < 0)
> +             return ret;
> +
> +     /* Turn on the display */
> +     ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
> +     if (ret < 0)
> +             return ret;
> +
> +     return 0;
> +}
> +
> +static int ssd1307fb_ssd1307_remove(struct ssd1307fb_par *par) {
> +     pwm_disable(par->pwm);
> +     pwm_put(par->pwm);
> +     return 0;
> +}
> +
> +static struct ssd1307fb_ops ssd1307fb_ssd1307_ops = {
> +     .init   = ssd1307fb_ssd1307_init,
> +     .remove = ssd1307fb_ssd1307_remove,
> +};
> +
> +static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par) {
> +     int ret;
> +
> +     /* Set initial contrast */
> +     ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
> +     ret = ret & ssd1307fb_write_cmd(par->client, 0x7f);
> +     if (ret < 0)
> +             return ret;
> +
> +     /* Set COM direction */
> +     ret = ssd1307fb_write_cmd(par->client, 0xc8);
> +     if (ret < 0)
> +             return ret;
> +
> +     /* Set segment re-map */
> +     ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
> +     if (ret < 0)
> +             return ret;
> +
> +     /* Set multiplex ratio value */
> +     ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_MULTIPLEX_RATIO);
> +     ret = ret & ssd1307fb_write_cmd(par->client, par->height - 1);
> +     if (ret < 0)
> +             return ret;
> +
> +     /* set display offset value */
> +     ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_DISPLAY_OFFSET);
> +     ret = ssd1307fb_write_cmd(par->client, 0x20);
> +     if (ret < 0)
> +             return ret;
> +
> +     /* Set clock frequency */
> +     ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_CLOCK_FREQ);
> +     ret = ret & ssd1307fb_write_cmd(par->client, 0xf0);
> +     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);
> +     ret = ret & ssd1307fb_write_cmd(par->client, 0x22);
> +     if (ret < 0)
> +             return ret;
> +
> +     /* Set COM pins configuration */
> +     ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COM_PINS_CONFIG);
> +     ret = ret & ssd1307fb_write_cmd(par->client, 0x22);
> +     if (ret < 0)
> +             return ret;
> +
> +     /* Set VCOMH */
> +     ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_VCOMH);
> +     ret = ret & ssd1307fb_write_cmd(par->client, 0x49);
> +     if (ret < 0)
> +             return ret;
> +
> +     /* Turn on the DC-DC Charge Pump */
> +     ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CHARGE_PUMP);
> +     ret = ret & ssd1307fb_write_cmd(par->client, 0x14);
> +     if (ret < 0)
> +             return ret;
> +
> +     /* Turn on the display */
> +     ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
> +     if (ret < 0)
> +             return ret;
> +
> +     return 0;
> +}
> +
> +static struct ssd1307fb_ops ssd1307fb_ssd1306_ops = {
> +     .init   = ssd1307fb_ssd1306_init,
> +};
> +
> +static const struct of_device_id ssd1307fb_of_match[] = {
> +     { .compatible = "solomon,ssd1306fb-i2c", .data = 
> (void*)&ssd1307fb_ssd1306_ops },
> +     { .compatible = "solomon,ssd1307fb-i2c", .data = 
> (void*)&ssd1307fb_ssd1307_ops },
> +     {},
> +};
> +MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
> +
>  static int ssd1307fb_probe(struct i2c_client *client,
>                          const struct i2c_device_id *id)
>  {
>       struct fb_info *info;
> -     u32 vmem_size = SSD1307FB_WIDTH * SSD1307FB_HEIGHT / 8;
> +     struct device_node *node = client->dev.of_node;
> +     u32 vmem_size;
>       struct ssd1307fb_par *par;
>       u8 *vmem;
>       int ret;
>  
> -     if (!client->dev.of_node) {
> +     if (!node) {
why this will be DT only?

a platform or ARN that does not support DT can not use this driver

this looks not right
>               dev_err(&client->dev, "No device tree data found!\n");
>               return -EINVAL;
>       }
> @@ -247,6 +378,36 @@ static int ssd1307fb_probe(struct i2c_client *client,
>               return -ENOMEM;
>       }
>  
> +     par = info->par;
> +     par->info = info;
> +     par->client = client;
> +
> +     par->ops = (struct ssd1307fb_ops*)of_match_device(ssd1307fb_of_match, 
> &client->dev)->data;
> +
> +     par->reset = of_get_named_gpio(client->dev.of_node,
> +                                      "reset-gpios", 0);
> +     if (!gpio_is_valid(par->reset)) {
> +             ret = -EINVAL;
> +             goto fb_alloc_error;
> +     }
> +
> +     if (of_property_read_u32(node, "solomon,width", &par->width))
> +             par->width = 96;
> +
> +     printk("Width\t%u\n", par->width);
> +
> +     if (of_property_read_u32(node, "solomon,height", &par->height))
> +             par->width = 16;
> +
> +     printk("Height\t%u\n", par->height);
> +
> +     if (of_property_read_u32(node, "solomon,page-offset", 
> &par->page_offset))
> +             par->page_offset = 1;
> +
> +     printk("Offset\t%u\n", par->page_offset);
> +
> +     vmem_size = par->width * par->height / 8;
> +
>       vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL);
>       if (!vmem) {
>               dev_err(&client->dev, "Couldn't allocate graphical memory.\n");
> @@ -256,9 +417,15 @@ static int ssd1307fb_probe(struct i2c_client *client,
>  
>       info->fbops = &ssd1307fb_ops;
>       info->fix = ssd1307fb_fix;
> +     info->fix.line_length = par->width / 8;
>       info->fbdefio = &ssd1307fb_defio;
>  
>       info->var = ssd1307fb_var;
> +     info->var.xres = par->width;
> +     info->var.xres_virtual = par->width;
> +     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;
> @@ -272,17 +439,6 @@ static int ssd1307fb_probe(struct i2c_client *client,
>  
>       fb_deferred_io_init(info);
>  
> -     par = info->par;
> -     par->info = info;
> -     par->client = client;
> -
> -     par->reset = of_get_named_gpio(client->dev.of_node,
> -                                      "reset-gpios", 0);
> -     if (!gpio_is_valid(par->reset)) {
> -             ret = -EINVAL;
> -             goto reset_oled_error;
> -     }
> -
>       ret = devm_gpio_request_one(&client->dev, par->reset,
>                                   GPIOF_OUT_INIT_HIGH,
>                                   "oled-reset");
> @@ -293,23 +449,6 @@ static int ssd1307fb_probe(struct i2c_client *client,
>               goto reset_oled_error;
>       }
>  
> -     par->pwm = pwm_get(&client->dev, NULL);
> -     if (IS_ERR(par->pwm)) {
> -             dev_err(&client->dev, "Could not get PWM from device tree!\n");
> -             ret = PTR_ERR(par->pwm);
> -             goto pwm_error;
> -     }
> -
> -     par->pwm_period = pwm_get_period(par->pwm);
> -
> -     dev_dbg(&client->dev, "Using PWM%d with a %dns period.\n", 
> par->pwm->pwm, par->pwm_period);
> -
> -     ret = register_framebuffer(info);
> -     if (ret) {
> -             dev_err(&client->dev, "Couldn't register the framebuffer\n");
> -             goto fbreg_error;
> -     }
> -
>       i2c_set_clientdata(client, info);
>  
>       /* Reset the screen */
> @@ -318,34 +457,25 @@ static int ssd1307fb_probe(struct i2c_client *client,
>       gpio_set_value(par->reset, 1);
>       udelay(4);
>  
> -     /* Enable the PWM */
> -     pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
> -     pwm_enable(par->pwm);
> -
> -     /* Map column 127 of the OLED to segment 0 */
> -     ret = ssd1307fb_write_cmd(client, SSD1307FB_SEG_REMAP_ON);
> -     if (ret < 0) {
> -             dev_err(&client->dev, "Couldn't remap the screen.\n");
> -             goto remap_error;
> +     if (par->ops->init) {
> +             ret = par->ops->init(par);
> +             if (ret)
> +                     goto reset_oled_error;
>       }
>  
> -     /* Turn on the display */
> -     ret = ssd1307fb_write_cmd(client, SSD1307FB_DISPLAY_ON);
> -     if (ret < 0) {
> -             dev_err(&client->dev, "Couldn't turn the display on.\n");
> -             goto remap_error;
> +     ret = register_framebuffer(info);
> +     if (ret) {
> +             dev_err(&client->dev, "Couldn't register the framebuffer\n");
> +             goto panel_init_error;
>       }
>  
>       dev_info(&client->dev, "fb%d: %s framebuffer device registered, using 
> %d bytes of video memory\n", info->node, info->fix.id, vmem_size);
>  
>       return 0;
>  
> -remap_error:
> -     unregister_framebuffer(info);
> -     pwm_disable(par->pwm);
> -fbreg_error:
> -     pwm_put(par->pwm);
> -pwm_error:
> +panel_init_error:
> +     if (par->ops->remove)
> +             par->ops->remove(par);
>  reset_oled_error:
>       fb_deferred_io_cleanup(info);
>  fb_alloc_error:
> @@ -359,8 +489,8 @@ static int ssd1307fb_remove(struct i2c_client *client)
>       struct ssd1307fb_par *par = info->par;
>  
>       unregister_framebuffer(info);
> -     pwm_disable(par->pwm);
> -     pwm_put(par->pwm);
> +     if (par->ops->remove)
> +             par->ops->remove(par);
>       fb_deferred_io_cleanup(info);
>       framebuffer_release(info);
>  
> @@ -368,17 +498,12 @@ static int ssd1307fb_remove(struct i2c_client *client)
>  }
>  
>  static const struct i2c_device_id ssd1307fb_i2c_id[] = {
> +     { "ssd1306fb", 0 },
>       { "ssd1307fb", 0 },
>       { }
>  };
>  MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id);
>  
> -static const struct of_device_id ssd1307fb_of_match[] = {
> -     { .compatible = "solomon,ssd1307fb-i2c" },
> -     {},
> -};
> -MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
> -
>  static struct i2c_driver ssd1307fb_driver = {
>       .probe = ssd1307fb_probe,
>       .remove = ssd1307fb_remove,
> -- 
> 1.7.10.4
> 
> _______________________________________________
> devicetree-discuss mailing list
> [email protected]
> https://lists.ozlabs.org/listinfo/devicetree-discuss
_______________________________________________
devicetree-discuss mailing list
[email protected]
https://lists.ozlabs.org/listinfo/devicetree-discuss

Reply via email to