|
|
|
@ -1,6 +1,6 @@
|
|
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
|
/*
|
|
|
|
|
* DRM driver for Solomon SSD130x OLED displays
|
|
|
|
|
* DRM driver for Solomon SSD13xx OLED displays
|
|
|
|
|
*
|
|
|
|
|
* Copyright 2022 Red Hat Inc.
|
|
|
|
|
* Author: Javier Martinez Canillas <javierm@redhat.com>
|
|
|
|
@ -37,13 +37,15 @@
|
|
|
|
|
#include "ssd130x.h"
|
|
|
|
|
|
|
|
|
|
#define DRIVER_NAME "ssd130x"
|
|
|
|
|
#define DRIVER_DESC "DRM driver for Solomon SSD130x OLED displays"
|
|
|
|
|
#define DRIVER_DESC "DRM driver for Solomon SSD13xx OLED displays"
|
|
|
|
|
#define DRIVER_DATE "20220131"
|
|
|
|
|
#define DRIVER_MAJOR 1
|
|
|
|
|
#define DRIVER_MINOR 0
|
|
|
|
|
|
|
|
|
|
#define SSD130X_PAGE_HEIGHT 8
|
|
|
|
|
|
|
|
|
|
#define SSD132X_SEGMENT_WIDTH 2
|
|
|
|
|
|
|
|
|
|
/* ssd13xx commands */
|
|
|
|
|
#define SSD13XX_CONTRAST 0x81
|
|
|
|
|
#define SSD13XX_SET_SEG_REMAP 0xa0
|
|
|
|
@ -99,6 +101,24 @@
|
|
|
|
|
#define SSD130X_SET_AREA_COLOR_MODE_ENABLE 0x1e
|
|
|
|
|
#define SSD130X_SET_AREA_COLOR_MODE_LOW_POWER 0x05
|
|
|
|
|
|
|
|
|
|
/* ssd132x commands */
|
|
|
|
|
#define SSD132X_SET_COL_RANGE 0x15
|
|
|
|
|
#define SSD132X_SET_DEACTIVATE_SCROLL 0x2e
|
|
|
|
|
#define SSD132X_SET_ROW_RANGE 0x75
|
|
|
|
|
#define SSD132X_SET_DISPLAY_START 0xa1
|
|
|
|
|
#define SSD132X_SET_DISPLAY_OFFSET 0xa2
|
|
|
|
|
#define SSD132X_SET_DISPLAY_NORMAL 0xa4
|
|
|
|
|
#define SSD132X_SET_FUNCTION_SELECT_A 0xab
|
|
|
|
|
#define SSD132X_SET_PHASE_LENGTH 0xb1
|
|
|
|
|
#define SSD132X_SET_CLOCK_FREQ 0xb3
|
|
|
|
|
#define SSD132X_SET_GPIO 0xb5
|
|
|
|
|
#define SSD132X_SET_PRECHARGE_PERIOD 0xb6
|
|
|
|
|
#define SSD132X_SET_GRAY_SCALE_TABLE 0xb8
|
|
|
|
|
#define SSD132X_SELECT_DEFAULT_TABLE 0xb9
|
|
|
|
|
#define SSD132X_SET_PRECHARGE_VOLTAGE 0xbc
|
|
|
|
|
#define SSD130X_SET_VCOMH_VOLTAGE 0xbe
|
|
|
|
|
#define SSD132X_SET_FUNCTION_SELECT_B 0xd5
|
|
|
|
|
|
|
|
|
|
#define MAX_CONTRAST 255
|
|
|
|
|
|
|
|
|
|
const struct ssd130x_deviceinfo ssd130x_variants[] = {
|
|
|
|
@ -144,6 +164,22 @@ const struct ssd130x_deviceinfo ssd130x_variants[] = {
|
|
|
|
|
.default_width = 128,
|
|
|
|
|
.default_height = 64,
|
|
|
|
|
.family_id = SSD130X_FAMILY,
|
|
|
|
|
},
|
|
|
|
|
/* ssd132x family */
|
|
|
|
|
[SSD1322_ID] = {
|
|
|
|
|
.default_width = 480,
|
|
|
|
|
.default_height = 128,
|
|
|
|
|
.family_id = SSD132X_FAMILY,
|
|
|
|
|
},
|
|
|
|
|
[SSD1325_ID] = {
|
|
|
|
|
.default_width = 128,
|
|
|
|
|
.default_height = 80,
|
|
|
|
|
.family_id = SSD132X_FAMILY,
|
|
|
|
|
},
|
|
|
|
|
[SSD1327_ID] = {
|
|
|
|
|
.default_width = 128,
|
|
|
|
|
.default_height = 128,
|
|
|
|
|
.family_id = SSD132X_FAMILY,
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
EXPORT_SYMBOL_NS_GPL(ssd130x_variants, DRM_SSD130X);
|
|
|
|
@ -463,6 +499,96 @@ static int ssd130x_init(struct ssd130x_device *ssd130x)
|
|
|
|
|
SSD130X_SET_ADDRESS_MODE_HORIZONTAL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ssd132x_init(struct ssd130x_device *ssd130x)
|
|
|
|
|
{
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
/* Set initial contrast */
|
|
|
|
|
ret = ssd130x_write_cmd(ssd130x, 2, SSD13XX_CONTRAST, 0x80);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
/* Set column start and end */
|
|
|
|
|
ret = ssd130x_write_cmd(ssd130x, 3, SSD132X_SET_COL_RANGE, 0x00,
|
|
|
|
|
ssd130x->width / SSD132X_SEGMENT_WIDTH - 1);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
/* Set row start and end */
|
|
|
|
|
ret = ssd130x_write_cmd(ssd130x, 3, SSD132X_SET_ROW_RANGE, 0x00, ssd130x->height - 1);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
/*
|
|
|
|
|
* Horizontal Address Increment
|
|
|
|
|
* Re-map for Column Address, Nibble and COM
|
|
|
|
|
* COM Split Odd Even
|
|
|
|
|
*/
|
|
|
|
|
ret = ssd130x_write_cmd(ssd130x, 2, SSD13XX_SET_SEG_REMAP, 0x53);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
/* Set display start and offset */
|
|
|
|
|
ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_DISPLAY_START, 0x00);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_DISPLAY_OFFSET, 0x00);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
/* Set display mode normal */
|
|
|
|
|
ret = ssd130x_write_cmd(ssd130x, 1, SSD132X_SET_DISPLAY_NORMAL);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
/* Set multiplex ratio value */
|
|
|
|
|
ret = ssd130x_write_cmd(ssd130x, 2, SSD13XX_SET_MULTIPLEX_RATIO, ssd130x->height - 1);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
/* Set phase length */
|
|
|
|
|
ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_PHASE_LENGTH, 0x55);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
/* Select default linear gray scale table */
|
|
|
|
|
ret = ssd130x_write_cmd(ssd130x, 1, SSD132X_SELECT_DEFAULT_TABLE);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
/* Set clock frequency */
|
|
|
|
|
ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_CLOCK_FREQ, 0x01);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
/* Enable internal VDD regulator */
|
|
|
|
|
ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_FUNCTION_SELECT_A, 0x1);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
/* Set pre-charge period */
|
|
|
|
|
ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_PRECHARGE_PERIOD, 0x01);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
/* Set pre-charge voltage */
|
|
|
|
|
ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_PRECHARGE_VOLTAGE, 0x08);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
/* Set VCOMH voltage */
|
|
|
|
|
ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_VCOMH_VOLTAGE, 0x07);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
/* Enable second pre-charge and internal VSL */
|
|
|
|
|
ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_FUNCTION_SELECT_B, 0x62);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ssd130x_update_rect(struct ssd130x_device *ssd130x,
|
|
|
|
|
struct drm_rect *rect, u8 *buf,
|
|
|
|
|
u8 *data_array)
|
|
|
|
@ -569,6 +695,64 @@ static int ssd130x_update_rect(struct ssd130x_device *ssd130x,
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ssd132x_update_rect(struct ssd130x_device *ssd130x,
|
|
|
|
|
struct drm_rect *rect, u8 *buf,
|
|
|
|
|
u8 *data_array)
|
|
|
|
|
{
|
|
|
|
|
unsigned int x = rect->x1;
|
|
|
|
|
unsigned int y = rect->y1;
|
|
|
|
|
unsigned int segment_width = SSD132X_SEGMENT_WIDTH;
|
|
|
|
|
unsigned int width = drm_rect_width(rect);
|
|
|
|
|
unsigned int height = drm_rect_height(rect);
|
|
|
|
|
unsigned int columns = DIV_ROUND_UP(width, segment_width);
|
|
|
|
|
unsigned int rows = height;
|
|
|
|
|
struct drm_device *drm = &ssd130x->drm;
|
|
|
|
|
u32 array_idx = 0;
|
|
|
|
|
unsigned int i, j;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
drm_WARN_ONCE(drm, x % segment_width != 0, "x must be aligned to screen segment\n");
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The screen is divided in Segment and Common outputs, where
|
|
|
|
|
* COM0 to COM[N - 1] are the rows and SEG0 to SEG[M - 1] are
|
|
|
|
|
* the columns.
|
|
|
|
|
*
|
|
|
|
|
* Each Segment has a 4-bit pixel and each Common output has a
|
|
|
|
|
* row of pixels. When using the (default) horizontal address
|
|
|
|
|
* increment mode, each byte of data sent to the controller has
|
|
|
|
|
* two Segments (e.g: SEG0 and SEG1) that are stored in the lower
|
|
|
|
|
* and higher nibbles of a single byte representing one column.
|
|
|
|
|
* That is, the first byte are SEG0 (D0[3:0]) and SEG1 (D0[7:4]),
|
|
|
|
|
* the second byte are SEG2 (D1[3:0]) and SEG3 (D1[7:4]) and so on.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* Set column start and end */
|
|
|
|
|
ret = ssd130x_write_cmd(ssd130x, 3, SSD132X_SET_COL_RANGE, x / segment_width, columns - 1);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
/* Set row start and end */
|
|
|
|
|
ret = ssd130x_write_cmd(ssd130x, 3, SSD132X_SET_ROW_RANGE, y, rows - 1);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < height; i++) {
|
|
|
|
|
/* Process pair of pixels and combine them into a single byte */
|
|
|
|
|
for (j = 0; j < width; j += segment_width) {
|
|
|
|
|
u8 n1 = buf[i * width + j];
|
|
|
|
|
u8 n2 = buf[i * width + j + 1];
|
|
|
|
|
|
|
|
|
|
data_array[array_idx++] = (n2 << 4) | n1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Write out update in one go since horizontal addressing mode is used */
|
|
|
|
|
ret = ssd130x_write_data(ssd130x, data_array, columns * rows);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ssd130x_clear_screen(struct ssd130x_device *ssd130x, u8 *data_array)
|
|
|
|
|
{
|
|
|
|
|
unsigned int pages = DIV_ROUND_UP(ssd130x->height, SSD130X_PAGE_HEIGHT);
|
|
|
|
@ -610,6 +794,17 @@ static void ssd130x_clear_screen(struct ssd130x_device *ssd130x, u8 *data_array)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ssd132x_clear_screen(struct ssd130x_device *ssd130x, u8 *data_array)
|
|
|
|
|
{
|
|
|
|
|
unsigned int columns = DIV_ROUND_UP(ssd130x->height, SSD132X_SEGMENT_WIDTH);
|
|
|
|
|
unsigned int height = ssd130x->height;
|
|
|
|
|
|
|
|
|
|
memset(data_array, 0, columns * height);
|
|
|
|
|
|
|
|
|
|
/* Write out update in one go since horizontal addressing mode is used */
|
|
|
|
|
ssd130x_write_data(ssd130x, data_array, columns * height);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb,
|
|
|
|
|
const struct iosys_map *vmap,
|
|
|
|
|
struct drm_rect *rect,
|
|
|
|
@ -640,6 +835,35 @@ static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb,
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ssd132x_fb_blit_rect(struct drm_framebuffer *fb,
|
|
|
|
|
const struct iosys_map *vmap,
|
|
|
|
|
struct drm_rect *rect, u8 *buf,
|
|
|
|
|
u8 *data_array)
|
|
|
|
|
{
|
|
|
|
|
struct ssd130x_device *ssd130x = drm_to_ssd130x(fb->dev);
|
|
|
|
|
unsigned int dst_pitch = drm_rect_width(rect);
|
|
|
|
|
struct iosys_map dst;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
/* Align x to display segment boundaries */
|
|
|
|
|
rect->x1 = round_down(rect->x1, SSD132X_SEGMENT_WIDTH);
|
|
|
|
|
rect->x2 = min_t(unsigned int, round_up(rect->x2, SSD132X_SEGMENT_WIDTH),
|
|
|
|
|
ssd130x->width);
|
|
|
|
|
|
|
|
|
|
ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
|
|
|
|
|
if (ret)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
iosys_map_set_vaddr(&dst, buf);
|
|
|
|
|
drm_fb_xrgb8888_to_gray8(&dst, &dst_pitch, vmap, fb, rect);
|
|
|
|
|
|
|
|
|
|
drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
|
|
|
|
|
|
|
|
|
|
ssd132x_update_rect(ssd130x, rect, buf, data_array);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ssd130x_primary_plane_atomic_check(struct drm_plane *plane,
|
|
|
|
|
struct drm_atomic_state *state)
|
|
|
|
|
{
|
|
|
|
@ -678,6 +902,44 @@ static int ssd130x_primary_plane_atomic_check(struct drm_plane *plane,
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ssd132x_primary_plane_atomic_check(struct drm_plane *plane,
|
|
|
|
|
struct drm_atomic_state *state)
|
|
|
|
|
{
|
|
|
|
|
struct drm_device *drm = plane->dev;
|
|
|
|
|
struct ssd130x_device *ssd130x = drm_to_ssd130x(drm);
|
|
|
|
|
struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
|
|
|
|
|
struct ssd130x_plane_state *ssd130x_state = to_ssd130x_plane_state(plane_state);
|
|
|
|
|
struct drm_crtc *crtc = plane_state->crtc;
|
|
|
|
|
struct drm_crtc_state *crtc_state;
|
|
|
|
|
const struct drm_format_info *fi;
|
|
|
|
|
unsigned int pitch;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
if (crtc)
|
|
|
|
|
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
|
|
|
|
|
|
|
|
|
ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
|
|
|
|
|
DRM_PLANE_NO_SCALING,
|
|
|
|
|
DRM_PLANE_NO_SCALING,
|
|
|
|
|
false, false);
|
|
|
|
|
if (ret)
|
|
|
|
|
return ret;
|
|
|
|
|
else if (!plane_state->visible)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
fi = drm_format_info(DRM_FORMAT_R8);
|
|
|
|
|
if (!fi)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
pitch = drm_format_info_min_pitch(fi, 0, ssd130x->width);
|
|
|
|
|
|
|
|
|
|
ssd130x_state->buffer = kcalloc(pitch, ssd130x->height, GFP_KERNEL);
|
|
|
|
|
if (!ssd130x_state->buffer)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ssd130x_primary_plane_atomic_update(struct drm_plane *plane,
|
|
|
|
|
struct drm_atomic_state *state)
|
|
|
|
|
{
|
|
|
|
@ -712,6 +974,40 @@ static void ssd130x_primary_plane_atomic_update(struct drm_plane *plane,
|
|
|
|
|
drm_dev_exit(idx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ssd132x_primary_plane_atomic_update(struct drm_plane *plane,
|
|
|
|
|
struct drm_atomic_state *state)
|
|
|
|
|
{
|
|
|
|
|
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_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc);
|
|
|
|
|
struct ssd130x_crtc_state *ssd130x_crtc_state = to_ssd130x_crtc_state(crtc_state);
|
|
|
|
|
struct ssd130x_plane_state *ssd130x_plane_state = to_ssd130x_plane_state(plane_state);
|
|
|
|
|
struct drm_framebuffer *fb = plane_state->fb;
|
|
|
|
|
struct drm_atomic_helper_damage_iter iter;
|
|
|
|
|
struct drm_device *drm = plane->dev;
|
|
|
|
|
struct drm_rect dst_clip;
|
|
|
|
|
struct drm_rect damage;
|
|
|
|
|
int idx;
|
|
|
|
|
|
|
|
|
|
if (!drm_dev_enter(drm, &idx))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
|
|
|
|
|
drm_atomic_for_each_plane_damage(&iter, &damage) {
|
|
|
|
|
dst_clip = plane_state->dst;
|
|
|
|
|
|
|
|
|
|
if (!drm_rect_intersect(&dst_clip, &damage))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
ssd132x_fb_blit_rect(fb, &shadow_plane_state->data[0], &dst_clip,
|
|
|
|
|
ssd130x_plane_state->buffer,
|
|
|
|
|
ssd130x_crtc_state->data_array);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
drm_dev_exit(idx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ssd130x_primary_plane_atomic_disable(struct drm_plane *plane,
|
|
|
|
|
struct drm_atomic_state *state)
|
|
|
|
|
{
|
|
|
|
@ -736,6 +1032,30 @@ static void ssd130x_primary_plane_atomic_disable(struct drm_plane *plane,
|
|
|
|
|
drm_dev_exit(idx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ssd132x_primary_plane_atomic_disable(struct drm_plane *plane,
|
|
|
|
|
struct drm_atomic_state *state)
|
|
|
|
|
{
|
|
|
|
|
struct drm_device *drm = plane->dev;
|
|
|
|
|
struct ssd130x_device *ssd130x = drm_to_ssd130x(drm);
|
|
|
|
|
struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
|
|
|
|
|
struct drm_crtc_state *crtc_state;
|
|
|
|
|
struct ssd130x_crtc_state *ssd130x_crtc_state;
|
|
|
|
|
int idx;
|
|
|
|
|
|
|
|
|
|
if (!plane_state->crtc)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc);
|
|
|
|
|
ssd130x_crtc_state = to_ssd130x_crtc_state(crtc_state);
|
|
|
|
|
|
|
|
|
|
if (!drm_dev_enter(drm, &idx))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
ssd132x_clear_screen(ssd130x, ssd130x_crtc_state->data_array);
|
|
|
|
|
|
|
|
|
|
drm_dev_exit(idx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Called during init to allocate the plane's atomic state. */
|
|
|
|
|
static void ssd130x_primary_plane_reset(struct drm_plane *plane)
|
|
|
|
|
{
|
|
|
|
@ -786,11 +1106,19 @@ static void ssd130x_primary_plane_destroy_state(struct drm_plane *plane,
|
|
|
|
|
kfree(ssd130x_state);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct drm_plane_helper_funcs ssd130x_primary_plane_helper_funcs = {
|
|
|
|
|
static const struct drm_plane_helper_funcs ssd130x_primary_plane_helper_funcs[] = {
|
|
|
|
|
[SSD130X_FAMILY] = {
|
|
|
|
|
DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
|
|
|
|
|
.atomic_check = ssd130x_primary_plane_atomic_check,
|
|
|
|
|
.atomic_update = ssd130x_primary_plane_atomic_update,
|
|
|
|
|
.atomic_disable = ssd130x_primary_plane_atomic_disable,
|
|
|
|
|
},
|
|
|
|
|
[SSD132X_FAMILY] = {
|
|
|
|
|
DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
|
|
|
|
|
.atomic_check = ssd132x_primary_plane_atomic_check,
|
|
|
|
|
.atomic_update = ssd132x_primary_plane_atomic_update,
|
|
|
|
|
.atomic_disable = ssd132x_primary_plane_atomic_disable,
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct drm_plane_funcs ssd130x_primary_plane_funcs = {
|
|
|
|
@ -839,6 +1167,27 @@ static int ssd130x_crtc_atomic_check(struct drm_crtc *crtc,
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ssd132x_crtc_atomic_check(struct drm_crtc *crtc,
|
|
|
|
|
struct drm_atomic_state *state)
|
|
|
|
|
{
|
|
|
|
|
struct drm_device *drm = crtc->dev;
|
|
|
|
|
struct ssd130x_device *ssd130x = drm_to_ssd130x(drm);
|
|
|
|
|
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
|
|
|
|
struct ssd130x_crtc_state *ssd130x_state = to_ssd130x_crtc_state(crtc_state);
|
|
|
|
|
unsigned int columns = DIV_ROUND_UP(ssd130x->width, SSD132X_SEGMENT_WIDTH);
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
ret = drm_crtc_helper_atomic_check(crtc, state);
|
|
|
|
|
if (ret)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
ssd130x_state->data_array = kmalloc(columns * ssd130x->height, GFP_KERNEL);
|
|
|
|
|
if (!ssd130x_state->data_array)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Called during init to allocate the CRTC's atomic state. */
|
|
|
|
|
static void ssd130x_crtc_reset(struct drm_crtc *crtc)
|
|
|
|
|
{
|
|
|
|
@ -891,9 +1240,15 @@ static void ssd130x_crtc_destroy_state(struct drm_crtc *crtc,
|
|
|
|
|
* 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 = {
|
|
|
|
|
static const struct drm_crtc_helper_funcs ssd130x_crtc_helper_funcs[] = {
|
|
|
|
|
[SSD130X_FAMILY] = {
|
|
|
|
|
.mode_valid = ssd130x_crtc_mode_valid,
|
|
|
|
|
.atomic_check = ssd130x_crtc_atomic_check,
|
|
|
|
|
},
|
|
|
|
|
[SSD132X_FAMILY] = {
|
|
|
|
|
.mode_valid = ssd130x_crtc_mode_valid,
|
|
|
|
|
.atomic_check = ssd132x_crtc_atomic_check,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct drm_crtc_funcs ssd130x_crtc_funcs = {
|
|
|
|
@ -931,6 +1286,31 @@ power_off:
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ssd132x_encoder_atomic_enable(struct drm_encoder *encoder,
|
|
|
|
|
struct drm_atomic_state *state)
|
|
|
|
|
{
|
|
|
|
|
struct drm_device *drm = encoder->dev;
|
|
|
|
|
struct ssd130x_device *ssd130x = drm_to_ssd130x(drm);
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
ret = ssd130x_power_on(ssd130x);
|
|
|
|
|
if (ret)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
ret = ssd132x_init(ssd130x);
|
|
|
|
|
if (ret)
|
|
|
|
|
goto power_off;
|
|
|
|
|
|
|
|
|
|
ssd130x_write_cmd(ssd130x, 1, SSD13XX_DISPLAY_ON);
|
|
|
|
|
|
|
|
|
|
backlight_enable(ssd130x->bl_dev);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
power_off:
|
|
|
|
|
ssd130x_power_off(ssd130x);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ssd130x_encoder_atomic_disable(struct drm_encoder *encoder,
|
|
|
|
|
struct drm_atomic_state *state)
|
|
|
|
|
{
|
|
|
|
@ -944,9 +1324,15 @@ static void ssd130x_encoder_atomic_disable(struct drm_encoder *encoder,
|
|
|
|
|
ssd130x_power_off(ssd130x);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct drm_encoder_helper_funcs ssd130x_encoder_helper_funcs = {
|
|
|
|
|
static const struct drm_encoder_helper_funcs ssd130x_encoder_helper_funcs[] = {
|
|
|
|
|
[SSD130X_FAMILY] = {
|
|
|
|
|
.atomic_enable = ssd130x_encoder_atomic_enable,
|
|
|
|
|
.atomic_disable = ssd130x_encoder_atomic_disable,
|
|
|
|
|
},
|
|
|
|
|
[SSD132X_FAMILY] = {
|
|
|
|
|
.atomic_enable = ssd132x_encoder_atomic_enable,
|
|
|
|
|
.atomic_disable = ssd130x_encoder_atomic_disable,
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct drm_encoder_funcs ssd130x_encoder_funcs = {
|
|
|
|
@ -1080,6 +1466,7 @@ static void ssd130x_parse_properties(struct ssd130x_device *ssd130x)
|
|
|
|
|
|
|
|
|
|
static int ssd130x_init_modeset(struct ssd130x_device *ssd130x)
|
|
|
|
|
{
|
|
|
|
|
enum ssd130x_family_ids family_id = ssd130x->device_info->family_id;
|
|
|
|
|
struct drm_display_mode *mode = &ssd130x->mode;
|
|
|
|
|
struct device *dev = ssd130x->dev;
|
|
|
|
|
struct drm_device *drm = &ssd130x->drm;
|
|
|
|
@ -1130,7 +1517,7 @@ static int ssd130x_init_modeset(struct ssd130x_device *ssd130x)
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
drm_plane_helper_add(primary_plane, &ssd130x_primary_plane_helper_funcs);
|
|
|
|
|
drm_plane_helper_add(primary_plane, &ssd130x_primary_plane_helper_funcs[family_id]);
|
|
|
|
|
|
|
|
|
|
drm_plane_enable_fb_damage_clips(primary_plane);
|
|
|
|
|
|
|
|
|
@ -1144,7 +1531,7 @@ static int ssd130x_init_modeset(struct ssd130x_device *ssd130x)
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
drm_crtc_helper_add(crtc, &ssd130x_crtc_helper_funcs);
|
|
|
|
|
drm_crtc_helper_add(crtc, &ssd130x_crtc_helper_funcs[family_id]);
|
|
|
|
|
|
|
|
|
|
/* Encoder */
|
|
|
|
|
|
|
|
|
@ -1156,7 +1543,7 @@ static int ssd130x_init_modeset(struct ssd130x_device *ssd130x)
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
drm_encoder_helper_add(encoder, &ssd130x_encoder_helper_funcs);
|
|
|
|
|
drm_encoder_helper_add(encoder, &ssd130x_encoder_helper_funcs[family_id]);
|
|
|
|
|
|
|
|
|
|
encoder->possible_crtcs = drm_crtc_mask(crtc);
|
|
|
|
|
|
|
|
|
|