Skip to content

Commit 45b5866

Browse files
drm/ssd130x: Allocate buffer in the plane's .atomic_check() callback
Drivers are not allowed to fail after drm_atomic_helper_swap_state() has been called and the new atomic state is stored into the current sw state. Since the struct ssd130x_device .data_array is allocated in the encoder's .atomic_enable callback, the operation can fail and this is after the new state has been stored. So it can break an atomic mode settings assumption. Fix this by having custom helpers to allocate, duplicate and destroy the plane state, that will take care of allocating and freeing these buffers. Suggested-by: Maxime Ripard <[email protected]> Signed-off-by: Javier Martinez Canillas <[email protected]> Acked-by: Maxime Ripard <[email protected]> Tested-by: Geert Uytterhoeven <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/[email protected]
1 parent 4cd179a commit 45b5866

File tree

2 files changed

+121
-40
lines changed

2 files changed

+121
-40
lines changed

drivers/gpu/drm/solomon/ssd130x.c

Lines changed: 121 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,19 @@ const struct ssd130x_deviceinfo ssd130x_variants[] = {
141141
};
142142
EXPORT_SYMBOL_NS_GPL(ssd130x_variants, DRM_SSD130X);
143143

144+
struct ssd130x_plane_state {
145+
struct drm_plane_state base;
146+
/* Intermediate buffer to convert pixels from XRGB8888 to HW format */
147+
u8 *buffer;
148+
/* Buffer to store pixels in HW format and written to the panel */
149+
u8 *data_array;
150+
};
151+
152+
static inline struct ssd130x_plane_state *to_ssd130x_plane_state(struct drm_plane_state *state)
153+
{
154+
return container_of(state, struct ssd130x_plane_state, base);
155+
}
156+
144157
static inline struct ssd130x_device *drm_to_ssd130x(struct drm_device *drm)
145158
{
146159
return container_of(drm, struct ssd130x_device, drm);
@@ -434,12 +447,14 @@ static int ssd130x_init(struct ssd130x_device *ssd130x)
434447
SSD130X_SET_ADDRESS_MODE_HORIZONTAL);
435448
}
436449

437-
static int ssd130x_update_rect(struct ssd130x_device *ssd130x, struct drm_rect *rect)
450+
static int ssd130x_update_rect(struct ssd130x_device *ssd130x,
451+
struct ssd130x_plane_state *ssd130x_state,
452+
struct drm_rect *rect)
438453
{
439454
unsigned int x = rect->x1;
440455
unsigned int y = rect->y1;
441-
u8 *buf = ssd130x->buffer;
442-
u8 *data_array = ssd130x->data_array;
456+
u8 *buf = ssd130x_state->buffer;
457+
u8 *data_array = ssd130x_state->data_array;
443458
unsigned int width = drm_rect_width(rect);
444459
unsigned int height = drm_rect_height(rect);
445460
unsigned int line_length = DIV_ROUND_UP(width, 8);
@@ -535,7 +550,8 @@ static int ssd130x_update_rect(struct ssd130x_device *ssd130x, struct drm_rect *
535550
return ret;
536551
}
537552

538-
static void ssd130x_clear_screen(struct ssd130x_device *ssd130x)
553+
static void ssd130x_clear_screen(struct ssd130x_device *ssd130x,
554+
struct ssd130x_plane_state *ssd130x_state)
539555
{
540556
struct drm_rect fullscreen = {
541557
.x1 = 0,
@@ -544,21 +560,21 @@ static void ssd130x_clear_screen(struct ssd130x_device *ssd130x)
544560
.y2 = ssd130x->height,
545561
};
546562

547-
ssd130x_update_rect(ssd130x, &fullscreen);
563+
ssd130x_update_rect(ssd130x, ssd130x_state, &fullscreen);
548564
}
549565

550-
static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb, const struct iosys_map *vmap,
566+
static int ssd130x_fb_blit_rect(struct drm_plane_state *state,
567+
const struct iosys_map *vmap,
551568
struct drm_rect *rect)
552569
{
570+
struct drm_framebuffer *fb = state->fb;
553571
struct ssd130x_device *ssd130x = drm_to_ssd130x(fb->dev);
554572
unsigned int page_height = ssd130x->device_info->page_height;
573+
struct ssd130x_plane_state *ssd130x_state = to_ssd130x_plane_state(state);
574+
u8 *buf = ssd130x_state->buffer;
555575
struct iosys_map dst;
556576
unsigned int dst_pitch;
557577
int ret = 0;
558-
u8 *buf = ssd130x->buffer;
559-
560-
if (!buf)
561-
return 0;
562578

563579
/* Align y to display page boundaries */
564580
rect->y1 = round_down(rect->y1, page_height);
@@ -575,11 +591,49 @@ static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb, const struct iosys_m
575591

576592
drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
577593

578-
ssd130x_update_rect(ssd130x, rect);
594+
ssd130x_update_rect(ssd130x, ssd130x_state, rect);
579595

580596
return ret;
581597
}
582598

599+
static int ssd130x_primary_plane_helper_atomic_check(struct drm_plane *plane,
600+
struct drm_atomic_state *state)
601+
{
602+
struct drm_device *drm = plane->dev;
603+
struct ssd130x_device *ssd130x = drm_to_ssd130x(drm);
604+
struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
605+
struct ssd130x_plane_state *ssd130x_state = to_ssd130x_plane_state(plane_state);
606+
unsigned int page_height = ssd130x->device_info->page_height;
607+
unsigned int pages = DIV_ROUND_UP(ssd130x->height, page_height);
608+
const struct drm_format_info *fi;
609+
unsigned int pitch;
610+
int ret;
611+
612+
ret = drm_plane_helper_atomic_check(plane, state);
613+
if (ret)
614+
return ret;
615+
616+
fi = drm_format_info(DRM_FORMAT_R1);
617+
if (!fi)
618+
return -EINVAL;
619+
620+
pitch = drm_format_info_min_pitch(fi, 0, ssd130x->width);
621+
622+
ssd130x_state->buffer = kcalloc(pitch, ssd130x->height, GFP_KERNEL);
623+
if (!ssd130x_state->buffer)
624+
return -ENOMEM;
625+
626+
ssd130x_state->data_array = kcalloc(ssd130x->width, pages, GFP_KERNEL);
627+
if (!ssd130x_state->data_array) {
628+
kfree(ssd130x_state->buffer);
629+
/* Set to prevent a double free in .atomic_destroy_state() */
630+
ssd130x_state->buffer = NULL;
631+
return -ENOMEM;
632+
}
633+
634+
return 0;
635+
}
636+
583637
static void ssd130x_primary_plane_helper_atomic_update(struct drm_plane *plane,
584638
struct drm_atomic_state *state)
585639
{
@@ -602,7 +656,7 @@ static void ssd130x_primary_plane_helper_atomic_update(struct drm_plane *plane,
602656
if (!drm_rect_intersect(&dst_clip, &damage))
603657
continue;
604658

605-
ssd130x_fb_blit_rect(plane_state->fb, &shadow_plane_state->data[0], &dst_clip);
659+
ssd130x_fb_blit_rect(plane_state, &shadow_plane_state->data[0], &dst_clip);
606660
}
607661

608662
drm_dev_exit(idx);
@@ -613,26 +667,79 @@ static void ssd130x_primary_plane_helper_atomic_disable(struct drm_plane *plane,
613667
{
614668
struct drm_device *drm = plane->dev;
615669
struct ssd130x_device *ssd130x = drm_to_ssd130x(drm);
670+
struct ssd130x_plane_state *ssd130x_state = to_ssd130x_plane_state(plane->state);
616671
int idx;
617672

618673
if (!drm_dev_enter(drm, &idx))
619674
return;
620675

621-
ssd130x_clear_screen(ssd130x);
676+
ssd130x_clear_screen(ssd130x, ssd130x_state);
622677

623678
drm_dev_exit(idx);
624679
}
625680

681+
/* Called during init to allocate the plane's atomic state. */
682+
static void ssd130x_primary_plane_reset(struct drm_plane *plane)
683+
{
684+
struct ssd130x_plane_state *ssd130x_state;
685+
686+
WARN_ON(plane->state);
687+
688+
ssd130x_state = kzalloc(sizeof(*ssd130x_state), GFP_KERNEL);
689+
if (!ssd130x_state)
690+
return;
691+
692+
__drm_atomic_helper_plane_reset(plane, &ssd130x_state->base);
693+
}
694+
695+
static struct drm_plane_state *ssd130x_primary_plane_duplicate_state(struct drm_plane *plane)
696+
{
697+
struct ssd130x_plane_state *old_ssd130x_state;
698+
struct ssd130x_plane_state *ssd130x_state;
699+
700+
if (WARN_ON(!plane->state))
701+
return NULL;
702+
703+
old_ssd130x_state = to_ssd130x_plane_state(plane->state);
704+
ssd130x_state = kmemdup(old_ssd130x_state, sizeof(*ssd130x_state), GFP_KERNEL);
705+
if (!ssd130x_state)
706+
return NULL;
707+
708+
/* The buffers are not duplicated and are allocated in .atomic_check */
709+
ssd130x_state->buffer = NULL;
710+
ssd130x_state->data_array = NULL;
711+
712+
__drm_atomic_helper_plane_duplicate_state(plane, &ssd130x_state->base);
713+
714+
return &ssd130x_state->base;
715+
}
716+
717+
static void ssd130x_primary_plane_destroy_state(struct drm_plane *plane,
718+
struct drm_plane_state *state)
719+
{
720+
struct ssd130x_plane_state *ssd130x_state = to_ssd130x_plane_state(state);
721+
722+
kfree(ssd130x_state->data_array);
723+
kfree(ssd130x_state->buffer);
724+
725+
__drm_atomic_helper_plane_destroy_state(&ssd130x_state->base);
726+
727+
kfree(ssd130x_state);
728+
}
729+
626730
static const struct drm_plane_helper_funcs ssd130x_primary_plane_helper_funcs = {
627731
DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
628-
.atomic_check = drm_plane_helper_atomic_check,
732+
.atomic_check = ssd130x_primary_plane_helper_atomic_check,
629733
.atomic_update = ssd130x_primary_plane_helper_atomic_update,
630734
.atomic_disable = ssd130x_primary_plane_helper_atomic_disable,
631735
};
632736

633737
static const struct drm_plane_funcs ssd130x_primary_plane_funcs = {
634738
.update_plane = drm_atomic_helper_update_plane,
635739
.disable_plane = drm_atomic_helper_disable_plane,
740+
.reset = ssd130x_primary_plane_reset,
741+
.atomic_duplicate_state = ssd130x_primary_plane_duplicate_state,
742+
.atomic_destroy_state = ssd130x_primary_plane_destroy_state,
636743
.destroy = drm_plane_cleanup,
637744
DRM_GEM_SHADOW_PLANE_FUNCS,
638745
};
@@ -677,10 +784,6 @@ static void ssd130x_encoder_helper_atomic_enable(struct drm_encoder *encoder,
677784
{
678785
struct drm_device *drm = encoder->dev;
679786
struct ssd130x_device *ssd130x = drm_to_ssd130x(drm);
680-
unsigned int page_height = ssd130x->device_info->page_height;
681-
unsigned int pages = DIV_ROUND_UP(ssd130x->height, page_height);
682-
const struct drm_format_info *fi;
683-
unsigned int pitch;
684787
int ret;
685788

686789
ret = ssd130x_power_on(ssd130x);
@@ -691,22 +794,6 @@ static void ssd130x_encoder_helper_atomic_enable(struct drm_encoder *encoder,
691794
if (ret)
692795
goto power_off;
693796

694-
fi = drm_format_info(DRM_FORMAT_R1);
695-
if (!fi)
696-
goto power_off;
697-
698-
pitch = drm_format_info_min_pitch(fi, 0, ssd130x->width);
699-
700-
ssd130x->buffer = kcalloc(pitch, ssd130x->height, GFP_KERNEL);
701-
if (!ssd130x->buffer)
702-
goto power_off;
703-
704-
ssd130x->data_array = kcalloc(ssd130x->width, pages, GFP_KERNEL);
705-
if (!ssd130x->data_array) {
706-
kfree(ssd130x->buffer);
707-
goto power_off;
708-
}
709-
710797
ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_ON);
711798

712799
backlight_enable(ssd130x->bl_dev);
@@ -728,9 +815,6 @@ static void ssd130x_encoder_helper_atomic_disable(struct drm_encoder *encoder,
728815

729816
ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_OFF);
730817

731-
kfree(ssd130x->data_array);
732-
kfree(ssd130x->buffer);
733-
734818
ssd130x_power_off(ssd130x);
735819
}
736820

drivers/gpu/drm/solomon/ssd130x.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,6 @@ struct ssd130x_device {
8989
u8 col_end;
9090
u8 page_start;
9191
u8 page_end;
92-
93-
u8 *buffer;
94-
u8 *data_array;
9592
};
9693

9794
extern const struct ssd130x_deviceinfo ssd130x_variants[];

0 commit comments

Comments
 (0)