diff options
Diffstat (limited to 'drivers/gpu/drm/solomon/ssd130x.c')
-rw-r--r-- | drivers/gpu/drm/solomon/ssd130x.c | 263 |
1 files changed, 179 insertions, 84 deletions
diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c index 5a3e3b78cd9e..bc41a5ae810a 100644 --- a/drivers/gpu/drm/solomon/ssd130x.c +++ b/drivers/gpu/drm/solomon/ssd130x.c @@ -18,10 +18,10 @@ #include <linux/pwm.h> #include <linux/regulator/consumer.h> +#include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_damage_helper.h> #include <drm/drm_edid.h> -#include <drm/drm_fb_cma_helper.h> #include <drm/drm_fb_helper.h> #include <drm/drm_format_helper.h> #include <drm/drm_framebuffer.h> @@ -537,11 +537,11 @@ static void ssd130x_clear_screen(struct ssd130x_device *ssd130x) kfree(buf); } -static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb, const struct iosys_map *map, +static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb, const struct iosys_map *vmap, struct drm_rect *rect) { struct ssd130x_device *ssd130x = drm_to_ssd130x(fb->dev); - void *vmap = map->vaddr; /* TODO: Use mapping abstraction properly */ + struct iosys_map dst; unsigned int dst_pitch; int ret = 0; u8 *buf = NULL; @@ -555,127 +555,174 @@ static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb, const struct iosys_m if (!buf) return -ENOMEM; - drm_fb_xrgb8888_to_mono(buf, dst_pitch, vmap, fb, rect); + ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); + if (ret) + goto out_free; + + iosys_map_set_vaddr(&dst, buf); + drm_fb_xrgb8888_to_mono(&dst, &dst_pitch, vmap, fb, rect); + + drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); ssd130x_update_rect(ssd130x, buf, rect); +out_free: kfree(buf); return ret; } -static int ssd130x_display_pipe_mode_valid(struct drm_simple_display_pipe *pipe, - const struct drm_display_mode *mode) +static void ssd130x_primary_plane_helper_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) { - struct ssd130x_device *ssd130x = drm_to_ssd130x(pipe->crtc.dev); + struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); + struct drm_device *drm = plane->dev; + struct drm_rect src_clip, dst_clip; + int idx; - if (mode->hdisplay != ssd130x->mode.hdisplay && - mode->vdisplay != ssd130x->mode.vdisplay) - return MODE_ONE_SIZE; + if (!drm_atomic_helper_damage_merged(old_plane_state, plane_state, &src_clip)) + return; - if (mode->hdisplay != ssd130x->mode.hdisplay) - return MODE_ONE_WIDTH; + dst_clip = plane_state->dst; + if (!drm_rect_intersect(&dst_clip, &src_clip)) + return; - if (mode->vdisplay != ssd130x->mode.vdisplay) - return MODE_ONE_HEIGHT; + if (!drm_dev_enter(drm, &idx)) + return; - return MODE_OK; + ssd130x_fb_blit_rect(plane_state->fb, &shadow_plane_state->data[0], &dst_clip); + + drm_dev_exit(idx); } -static void ssd130x_display_pipe_enable(struct drm_simple_display_pipe *pipe, - struct drm_crtc_state *crtc_state, - struct drm_plane_state *plane_state) +static void ssd130x_primary_plane_helper_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *state) { - struct ssd130x_device *ssd130x = drm_to_ssd130x(pipe->crtc.dev); - struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); - struct drm_device *drm = &ssd130x->drm; - int idx, ret; + struct drm_device *drm = plane->dev; + struct ssd130x_device *ssd130x = drm_to_ssd130x(drm); + int idx; - ret = ssd130x_power_on(ssd130x); - if (ret) + if (!drm_dev_enter(drm, &idx)) return; - ret = ssd130x_init(ssd130x); - if (ret) - goto out_power_off; + ssd130x_clear_screen(ssd130x); - if (!drm_dev_enter(drm, &idx)) - goto out_power_off; + drm_dev_exit(idx); +} - ssd130x_fb_blit_rect(plane_state->fb, &shadow_plane_state->data[0], &plane_state->dst); +static const struct drm_plane_helper_funcs ssd130x_primary_plane_helper_funcs = { + DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, + .atomic_check = drm_plane_helper_atomic_check, + .atomic_update = ssd130x_primary_plane_helper_atomic_update, + .atomic_disable = ssd130x_primary_plane_helper_atomic_disable, +}; - ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_ON); +static const struct drm_plane_funcs ssd130x_primary_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + DRM_GEM_SHADOW_PLANE_FUNCS, +}; - backlight_enable(ssd130x->bl_dev); +static enum drm_mode_status ssd130x_crtc_helper_mode_valid(struct drm_crtc *crtc, + const struct drm_display_mode *mode) +{ + struct ssd130x_device *ssd130x = drm_to_ssd130x(crtc->dev); - drm_dev_exit(idx); + if (mode->hdisplay != ssd130x->mode.hdisplay && + mode->vdisplay != ssd130x->mode.vdisplay) + return MODE_ONE_SIZE; + else if (mode->hdisplay != ssd130x->mode.hdisplay) + return MODE_ONE_WIDTH; + else if (mode->vdisplay != ssd130x->mode.vdisplay) + return MODE_ONE_HEIGHT; - return; -out_power_off: - ssd130x_power_off(ssd130x); + return MODE_OK; } -static void ssd130x_display_pipe_disable(struct drm_simple_display_pipe *pipe) +static int ssd130x_crtc_helper_atomic_check(struct drm_crtc *crtc, + struct drm_atomic_state *new_state) { - struct ssd130x_device *ssd130x = drm_to_ssd130x(pipe->crtc.dev); - struct drm_device *drm = &ssd130x->drm; - int idx; + struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc); + int ret; - if (!drm_dev_enter(drm, &idx)) - return; + ret = drm_atomic_helper_check_crtc_state(new_crtc_state, false); + if (ret) + return ret; - ssd130x_clear_screen(ssd130x); + return drm_atomic_add_affected_planes(new_state, crtc); +} - backlight_disable(ssd130x->bl_dev); +/* + * The CRTC is always enabled. Screen updates are performed by + * the primary plane's atomic_update function. Disabling clears + * the screen in the primary plane's atomic_disable function. + */ +static const struct drm_crtc_helper_funcs ssd130x_crtc_helper_funcs = { + .mode_valid = ssd130x_crtc_helper_mode_valid, + .atomic_check = ssd130x_crtc_helper_atomic_check, +}; - ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_OFF); +static void ssd130x_crtc_reset(struct drm_crtc *crtc) +{ + struct drm_device *drm = crtc->dev; + struct ssd130x_device *ssd130x = drm_to_ssd130x(drm); - ssd130x_power_off(ssd130x); + ssd130x_init(ssd130x); - drm_dev_exit(idx); + drm_atomic_helper_crtc_reset(crtc); } -static void ssd130x_display_pipe_update(struct drm_simple_display_pipe *pipe, - struct drm_plane_state *old_plane_state) +static const struct drm_crtc_funcs ssd130x_crtc_funcs = { + .reset = ssd130x_crtc_reset, + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, +}; + +static void ssd130x_encoder_helper_atomic_enable(struct drm_encoder *encoder, + struct drm_atomic_state *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; + struct drm_device *drm = encoder->dev; + struct ssd130x_device *ssd130x = drm_to_ssd130x(drm); + int ret; - if (!fb) + ret = ssd130x_power_on(ssd130x); + if (ret) return; - if (!pipe->crtc.state->active) - return; + ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_ON); - if (!drm_atomic_helper_damage_merged(old_plane_state, plane_state, &src_clip)) - return; + backlight_enable(ssd130x->bl_dev); +} - dst_clip = plane_state->dst; - if (!drm_rect_intersect(&dst_clip, &src_clip)) - return; +static void ssd130x_encoder_helper_atomic_disable(struct drm_encoder *encoder, + struct drm_atomic_state *state) +{ + struct drm_device *drm = encoder->dev; + struct ssd130x_device *ssd130x = drm_to_ssd130x(drm); - if (!drm_dev_enter(drm, &idx)) - return; + backlight_disable(ssd130x->bl_dev); - ssd130x_fb_blit_rect(plane_state->fb, &shadow_plane_state->data[0], &dst_clip); + ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_OFF); - drm_dev_exit(idx); + ssd130x_power_off(ssd130x); } -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 const struct drm_encoder_helper_funcs ssd130x_encoder_helper_funcs = { + .atomic_enable = ssd130x_encoder_helper_atomic_enable, + .atomic_disable = ssd130x_encoder_helper_atomic_disable, +}; + +static const struct drm_encoder_funcs ssd130x_encoder_funcs = { + .destroy = drm_encoder_cleanup, }; -static int ssd130x_connector_get_modes(struct drm_connector *connector) +static int ssd130x_connector_helper_get_modes(struct drm_connector *connector) { struct ssd130x_device *ssd130x = drm_to_ssd130x(connector->dev); struct drm_display_mode *mode; @@ -695,7 +742,7 @@ static int ssd130x_connector_get_modes(struct drm_connector *connector) } static const struct drm_connector_helper_funcs ssd130x_connector_helper_funcs = { - .get_modes = ssd130x_connector_get_modes, + .get_modes = ssd130x_connector_helper_get_modes, }; static const struct drm_connector_funcs ssd130x_connector_funcs = { @@ -806,8 +853,16 @@ static int ssd130x_init_modeset(struct ssd130x_device *ssd130x) struct device *dev = ssd130x->dev; struct drm_device *drm = &ssd130x->drm; unsigned long max_width, max_height; + struct drm_plane *primary_plane; + struct drm_crtc *crtc; + struct drm_encoder *encoder; + struct drm_connector *connector; int ret; + /* + * Modesetting + */ + ret = drmm_mode_config_init(drm); if (ret) { dev_err(dev, "DRM mode config init failed: %d\n", ret); @@ -833,25 +888,65 @@ static int ssd130x_init_modeset(struct ssd130x_device *ssd130x) drm->mode_config.preferred_depth = 32; drm->mode_config.funcs = &ssd130x_mode_config_funcs; - ret = drm_connector_init(drm, &ssd130x->connector, &ssd130x_connector_funcs, + /* Primary plane */ + + primary_plane = &ssd130x->primary_plane; + ret = drm_universal_plane_init(drm, primary_plane, 0, &ssd130x_primary_plane_funcs, + ssd130x_formats, ARRAY_SIZE(ssd130x_formats), + NULL, DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) { + dev_err(dev, "DRM primary plane init failed: %d\n", ret); + return ret; + } + + drm_plane_helper_add(primary_plane, &ssd130x_primary_plane_helper_funcs); + + drm_plane_enable_fb_damage_clips(primary_plane); + + /* CRTC */ + + crtc = &ssd130x->crtc; + ret = drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL, + &ssd130x_crtc_funcs, NULL); + if (ret) { + dev_err(dev, "DRM crtc init failed: %d\n", ret); + return ret; + } + + drm_crtc_helper_add(crtc, &ssd130x_crtc_helper_funcs); + + /* Encoder */ + + encoder = &ssd130x->encoder; + ret = drm_encoder_init(drm, encoder, &ssd130x_encoder_funcs, + DRM_MODE_ENCODER_NONE, NULL); + if (ret) { + dev_err(dev, "DRM encoder init failed: %d\n", ret); + return ret; + } + + drm_encoder_helper_add(encoder, &ssd130x_encoder_helper_funcs); + + encoder->possible_crtcs = drm_crtc_mask(crtc); + + /* Connector */ + + connector = &ssd130x->connector; + ret = drm_connector_init(drm, 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); + drm_connector_helper_add(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); + ret = drm_connector_attach_encoder(connector, encoder); if (ret) { - dev_err(dev, "DRM simple display pipeline init failed: %d\n", ret); + dev_err(dev, "DRM attach connector to encoder failed: %d\n", ret); return ret; } - drm_plane_enable_fb_damage_clips(&ssd130x->pipe.plane); - drm_mode_config_reset(drm); return 0; |