[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <cfaab4fc-f646-e38e-be57-8a6142ca28ec@suse.de>
Date: Fri, 11 Feb 2022 13:44:30 +0100
From: Thomas Zimmermann <tzimmermann@...e.de>
To: Javier Martinez Canillas <javierm@...hat.com>,
linux-kernel@...r.kernel.org
Cc: linux-pwm@...r.kernel.org, linux-fbdev@...r.kernel.org,
David Airlie <airlied@...ux.ie>,
Daniel Vetter <daniel.vetter@...ll.ch>,
dri-devel@...ts.freedesktop.org,
Noralf Trønnes <noralf@...nnes.org>,
Geert Uytterhoeven <geert@...ux-m68k.org>,
Maxime Ripard <maxime@...no.tech>,
Uwe Kleine-König <u.kleine-koenig@...gutronix.de>,
Thierry Reding <thierry.reding@...il.com>,
Lee Jones <lee.jones@...aro.org>,
Andy Shevchenko <andriy.shevchenko@...ux.intel.com>,
Sam Ravnborg <sam@...nborg.org>
Subject: Re: [PATCH v4 3/6] drm: Add driver for Solomon SSD130x OLED displays
Hi
Am 11.02.22 um 10:19 schrieb Javier Martinez Canillas:
...
> +
> +static void ssd130x_display_pipe_enable(struct drm_simple_display_pipe *pipe,
> + struct drm_crtc_state *crtc_state,
> + struct drm_plane_state *plane_state)
> +{
> + struct ssd130x_device *ssd130x = drm_to_ssd130x(pipe->crtc.dev);
> + struct drm_device *drm = &ssd130x->drm;
> + int idx, ret;
> +
> + ret = ssd130x_power_on(ssd130x);
> + if (ret)
> + return;
> +
> + ret = ssd130x_init(ssd130x);
> + if (ret)
> + goto out_power_off;
> +
> + if (!drm_dev_enter(drm, &idx))
> + goto out_power_off;
> +
> + ssd130x_clear_screen(ssd130x);
Rather than clearing the screen, send the whole fb onto the display.
Best regards
Thomas
> +
> + ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_ON);
> +
> + backlight_enable(ssd130x->bl_dev);
> +
> + drm_dev_exit(idx);
> +
> + return;
> +out_power_off:
> + ssd130x_power_off(ssd130x);
> +}
> +
> +static void ssd130x_display_pipe_disable(struct drm_simple_display_pipe *pipe)
> +{
> + struct ssd130x_device *ssd130x = drm_to_ssd130x(pipe->crtc.dev);
> + struct drm_device *drm = &ssd130x->drm;
> + int idx;
> +
> + if (!drm_dev_enter(drm, &idx))
> + return;
> +
> + ssd130x_clear_screen(ssd130x);
> +
> + backlight_disable(ssd130x->bl_dev);
> +
> + ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_OFF);
> +
> + ssd130x_power_off(ssd130x);
> +
> + drm_dev_exit(idx);
> +}
> +
> +static void ssd130x_display_pipe_update(struct drm_simple_display_pipe *pipe,
> + struct drm_plane_state *old_plane_state)
> +{
> + struct ssd130x_device *ssd130x = drm_to_ssd130x(pipe->crtc.dev);
> + struct drm_plane_state *plane_state = pipe->plane.state;
> + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
> + struct drm_framebuffer *fb = plane_state->fb;
> + struct drm_device *drm = &ssd130x->drm;
> + struct drm_rect src_clip, dst_clip;
> + int idx;
> +
> + if (!fb)
> + return;
> +
> + if (!pipe->crtc.state->active)
> + return;
> +
> + if (!drm_atomic_helper_damage_merged(old_plane_state, plane_state, &src_clip))
> + return;
> +
> + dst_clip = plane_state->dst;
> + if (!drm_rect_intersect(&dst_clip, &src_clip))
> + return;
> +
> + if (!drm_dev_enter(drm, &idx))
> + return;
> +
> + ssd130x_fb_blit_rect(plane_state->fb, &shadow_plane_state->data[0], &dst_clip);
> +
> + drm_dev_exit(idx);
> +}
> +
> +static const struct drm_simple_display_pipe_funcs ssd130x_pipe_funcs = {
> + .mode_valid = ssd130x_display_pipe_mode_valid,
> + .enable = ssd130x_display_pipe_enable,
> + .disable = ssd130x_display_pipe_disable,
> + .update = ssd130x_display_pipe_update,
> + DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS,
> +};
> +
> +static int ssd130x_connector_get_modes(struct drm_connector *connector)
> +{
> + struct ssd130x_device *ssd130x = drm_to_ssd130x(connector->dev);
> + struct drm_display_mode *mode = &ssd130x->mode;
> + struct device *dev = ssd130x->dev;
> +
> + mode = drm_mode_duplicate(connector->dev, &ssd130x->mode);
> + if (!mode) {
> + dev_err(dev, "Failed to duplicated mode\n");
> + return 0;
> + }
> +
> + drm_mode_probed_add(connector, mode);
> + drm_set_preferred_mode(connector, mode->hdisplay, mode->vdisplay);
> +
> + /* There is only a single mode */
> + return 1;
> +}
> +
> +static const struct drm_connector_helper_funcs ssd130x_connector_helper_funcs = {
> + .get_modes = ssd130x_connector_get_modes,
> +};
> +
> +static const struct drm_connector_funcs ssd130x_connector_funcs = {
> + .reset = drm_atomic_helper_connector_reset,
> + .fill_modes = drm_helper_probe_single_connector_modes,
> + .destroy = drm_connector_cleanup,
> + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static const struct drm_mode_config_funcs ssd130x_mode_config_funcs = {
> + .fb_create = drm_gem_fb_create_with_dirty,
> + .atomic_check = drm_atomic_helper_check,
> + .atomic_commit = drm_atomic_helper_commit,
> +};
> +
> +static const uint32_t ssd130x_formats[] = {
> + DRM_FORMAT_XRGB8888,
> +};
> +
> +DEFINE_DRM_GEM_FOPS(ssd130x_fops);
> +
> +static const struct drm_driver ssd130x_drm_driver = {
> + DRM_GEM_SHMEM_DRIVER_OPS,
> + .name = DRIVER_NAME,
> + .desc = DRIVER_DESC,
> + .date = DRIVER_DATE,
> + .major = DRIVER_MAJOR,
> + .minor = DRIVER_MINOR,
> + .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,
> + .fops = &ssd130x_fops,
> +};
> +
> +static int ssd130x_update_bl(struct backlight_device *bdev)
> +{
> + struct ssd130x_device *ssd130x = bl_get_data(bdev);
> + int brightness = backlight_get_brightness(bdev);
> + int ret;
> +
> + ssd130x->contrast = brightness;
> +
> + ret = ssd130x_write_cmd(ssd130x, 1, SSD130X_CONTRAST);
> + if (ret < 0)
> + return ret;
> +
> + ret = ssd130x_write_cmd(ssd130x, 1, ssd130x->contrast);
> + if (ret < 0)
> + return ret;
> +
> + return 0;
> +}
> +
> +static const struct backlight_ops ssd130xfb_bl_ops = {
> + .update_status = ssd130x_update_bl,
> +};
> +
> +static void ssd130x_parse_properties(struct ssd130x_device *ssd130x)
> +{
> + struct device *dev = ssd130x->dev;
> +
> + if (device_property_read_u32(dev, "solomon,width", &ssd130x->width))
> + ssd130x->width = 96;
> +
> + if (device_property_read_u32(dev, "solomon,height", &ssd130x->height))
> + ssd130x->height = 16;
> +
> + if (device_property_read_u32(dev, "solomon,page-offset", &ssd130x->page_offset))
> + ssd130x->page_offset = 1;
> +
> + if (device_property_read_u32(dev, "solomon,col-offset", &ssd130x->col_offset))
> + ssd130x->col_offset = 0;
> +
> + if (device_property_read_u32(dev, "solomon,com-offset", &ssd130x->com_offset))
> + ssd130x->com_offset = 0;
> +
> + if (device_property_read_u32(dev, "solomon,prechargep1", &ssd130x->prechargep1))
> + ssd130x->prechargep1 = 2;
> +
> + if (device_property_read_u32(dev, "solomon,prechargep2", &ssd130x->prechargep2))
> + ssd130x->prechargep2 = 2;
> +
> + if (!device_property_read_u8_array(dev, "solomon,lookup-table",
> + ssd130x->lookup_table,
> + ARRAY_SIZE(ssd130x->lookup_table)))
> + ssd130x->lookup_table_set = 1;
> +
> + ssd130x->seg_remap = !device_property_read_bool(dev, "solomon,segment-no-remap");
> + ssd130x->com_seq = device_property_read_bool(dev, "solomon,com-seq");
> + ssd130x->com_lrremap = device_property_read_bool(dev, "solomon,com-lrremap");
> + ssd130x->com_invdir = device_property_read_bool(dev, "solomon,com-invdir");
> + ssd130x->area_color_enable =
> + device_property_read_bool(dev, "solomon,area-color-enable");
> + ssd130x->low_power = device_property_read_bool(dev, "solomon,low-power");
> +
> + ssd130x->contrast = 127;
> + ssd130x->vcomh = ssd130x->device_info->default_vcomh;
> +
> + /* Setup display timing */
> + if (device_property_read_u32(dev, "solomon,dclk-div", &ssd130x->dclk_div))
> + ssd130x->dclk_div = ssd130x->device_info->default_dclk_div;
> + if (device_property_read_u32(dev, "solomon,dclk-frq", &ssd130x->dclk_frq))
> + ssd130x->dclk_frq = ssd130x->device_info->default_dclk_frq;
> +}
> +
> +static int ssd130x_init_modeset(struct ssd130x_device *ssd130x)
> +{
> + struct drm_display_mode *mode = &ssd130x->mode;
> + struct device *dev = ssd130x->dev;
> + struct drm_device *drm = &ssd130x->drm;
> + unsigned long max_width, max_height;
> + int ret;
> +
> + ret = drmm_mode_config_init(drm);
> + if (ret) {
> + dev_err(dev, "DRM mode config init failed: %d\n", ret);
> + return ret;
> + }
> +
> + mode->type = DRM_MODE_TYPE_DRIVER;
> + mode->clock = 1;
> + mode->hdisplay = mode->htotal = ssd130x->width;
> + mode->hsync_start = mode->hsync_end = ssd130x->width;
> + mode->vdisplay = mode->vtotal = ssd130x->height;
> + mode->vsync_start = mode->vsync_end = ssd130x->height;
> + mode->width_mm = 27;
> + mode->height_mm = 27;
> +
> + max_width = max_t(unsigned long, mode->hdisplay, DRM_SHADOW_PLANE_MAX_WIDTH);
> + max_height = max_t(unsigned long, mode->vdisplay, DRM_SHADOW_PLANE_MAX_HEIGHT);
> +
> + drm->mode_config.min_width = mode->hdisplay;
> + drm->mode_config.max_width = max_width;
> + drm->mode_config.min_height = mode->vdisplay;
> + drm->mode_config.max_height = max_height;
> + drm->mode_config.preferred_depth = 32;
> + drm->mode_config.funcs = &ssd130x_mode_config_funcs;
> +
> + ret = drm_connector_init(drm, &ssd130x->connector, &ssd130x_connector_funcs,
> + DRM_MODE_CONNECTOR_Unknown);
> + if (ret) {
> + dev_err(dev, "DRM connector init failed: %d\n", ret);
> + return ret;
> + }
> +
> + drm_connector_helper_add(&ssd130x->connector, &ssd130x_connector_helper_funcs);
> +
> + ret = drm_simple_display_pipe_init(drm, &ssd130x->pipe, &ssd130x_pipe_funcs,
> + ssd130x_formats, ARRAY_SIZE(ssd130x_formats),
> + NULL, &ssd130x->connector);
> + if (ret) {
> + dev_err(dev, "DRM simple display pipeline init failed: %d\n", ret);
> + return ret;
> + }
> +
> + drm_plane_enable_fb_damage_clips(&ssd130x->pipe.plane);
> +
> + drm_mode_config_reset(drm);
> +
> + return 0;
> +}
> +
> +static int ssd130x_get_resources(struct ssd130x_device *ssd130x)
> +{
> + struct device *dev = ssd130x->dev;
> +
> + ssd130x->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
> + if (IS_ERR(ssd130x->reset))
> + return dev_err_probe(dev, PTR_ERR(ssd130x->reset),
> + "Failed to get reset gpio\n");
> +
> + ssd130x->vcc_reg = devm_regulator_get(dev, "vcc");
> + if (IS_ERR(ssd130x->vcc_reg))
> + return dev_err_probe(dev, PTR_ERR(ssd130x->vcc_reg),
> + "Failed to get VCC regulator\n");
> +
> + return 0;
> +}
> +
> +struct ssd130x_device *ssd130x_probe(struct device *dev, struct regmap *regmap)
> +{
> + struct ssd130x_device *ssd130x;
> + struct backlight_device *bl;
> + struct drm_device *drm;
> + int ret;
> +
> + ssd130x = devm_drm_dev_alloc(dev, &ssd130x_drm_driver,
> + struct ssd130x_device, drm);
> + if (IS_ERR(ssd130x)) {
> + dev_err_probe(dev, PTR_ERR(ssd130x),
> + "Failed to allocate DRM device\n");
> + return ssd130x;
> + }
> +
> + drm = &ssd130x->drm;
> +
> + ssd130x->dev = dev;
> + ssd130x->regmap = regmap;
> + ssd130x->device_info = device_get_match_data(dev);
> +
> + ssd130x_parse_properties(ssd130x);
> +
> + ret = ssd130x_get_resources(ssd130x);
> + if (ret)
> + return ERR_PTR(ret);
> +
> + bl = devm_backlight_device_register(dev, dev_name(dev), dev, ssd130x,
> + &ssd130xfb_bl_ops, NULL);
> + if (IS_ERR(bl)) {
> + ret = PTR_ERR(bl);
> + dev_err_probe(dev, ret, "Unable to register backlight device\n");
> + return ERR_PTR(ret);
> + }
> +
> + bl->props.brightness = ssd130x->contrast;
> + bl->props.max_brightness = MAX_CONTRAST;
> + ssd130x->bl_dev = bl;
> +
> + ret = ssd130x_init_modeset(ssd130x);
> + if (ret)
> + return ERR_PTR(ret);
> +
> + ret = drm_dev_register(drm, 0);
> + if (ret) {
> + dev_err_probe(dev, ret, "DRM device register failed\n");
> + return ERR_PTR(ret);
> + }
> +
> + drm_fbdev_generic_setup(drm, 0);
> +
> + return ssd130x;
> +}
> +EXPORT_SYMBOL_GPL(ssd130x_probe);
> +
> +int ssd130x_remove(struct ssd130x_device *ssd130x)
> +{
> + drm_dev_unplug(&ssd130x->drm);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(ssd130x_remove);
> +
> +void ssd130x_shutdown(struct ssd130x_device *ssd130x)
> +{
> + drm_atomic_helper_shutdown(&ssd130x->drm);
> +}
> +EXPORT_SYMBOL_GPL(ssd130x_shutdown);
> +
> +MODULE_DESCRIPTION(DRIVER_DESC);
> +MODULE_AUTHOR("Javier Martinez Canillas <javierm@...hat.com>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/solomon/ssd130x.h b/drivers/gpu/drm/solomon/ssd130x.h
> new file mode 100644
> index 000000000000..754953b261b0
> --- /dev/null
> +++ b/drivers/gpu/drm/solomon/ssd130x.h
> @@ -0,0 +1,76 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Header file for:
> + * DRM driver for Solomon SSD130x OLED displays
> + *
> + * Copyright 2022 Red Hat Inc.
> + * Authors: Javier Martinez Canillas <javierm@...hat.com>
> + *
> + * Based on drivers/video/fbdev/ssd1307fb.c
> + * Copyright 2012 Free Electrons
> + */
> +
> +#ifndef __SSD1307X_H__
> +#define __SSD1307X_H__
> +
> +#include <drm/drm_drv.h>
> +#include <drm/drm_simple_kms_helper.h>
> +
> +#include <linux/regmap.h>
> +
> +struct ssd130x_deviceinfo {
> + u32 default_vcomh;
> + u32 default_dclk_div;
> + u32 default_dclk_frq;
> + int need_pwm;
> + int need_chargepump;
> +};
> +
> +struct ssd130x_device {
> + struct drm_device drm;
> + struct device *dev;
> + struct drm_simple_display_pipe pipe;
> + struct drm_display_mode mode;
> + struct drm_connector connector;
> + struct i2c_client *client;
> +
> + struct regmap *regmap;
> +
> + const struct ssd130x_deviceinfo *device_info;
> +
> + 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 contrast;
> + u32 dclk_div;
> + u32 dclk_frq;
> + u32 height;
> + u8 lookup_table[4];
> + u32 page_offset;
> + u32 col_offset;
> + u32 prechargep1;
> + u32 prechargep2;
> +
> + struct backlight_device *bl_dev;
> + struct pwm_device *pwm;
> + struct gpio_desc *reset;
> + struct regulator *vcc_reg;
> + u32 vcomh;
> + u32 width;
> + /* Cached address ranges */
> + u8 col_start;
> + u8 col_end;
> + u8 page_start;
> + u8 page_end;
> +};
> +
> +struct ssd130x_device *ssd130x_probe(struct device *dev, struct regmap *regmap);
> +int ssd130x_remove(struct ssd130x_device *ssd130x);
> +void ssd130x_shutdown(struct ssd130x_device *ssd130x);
> +
> +#endif /* __SSD1307X_H__ */
--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev
Download attachment "OpenPGP_signature" of type "application/pgp-signature" (841 bytes)
Powered by blists - more mailing lists