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@redhat.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@redhat.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__ */