Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <mripard@kernel.org>
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
Acked-by: Maxime Ripard <mripard@kernel.org>
Tested-by: Geert Uytterhoeven <geert@linux-m68k.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20230726105433.389740-2-javierm@redhat.com

+121 -40
+121 -37
drivers/gpu/drm/solomon/ssd130x.c
··· 141 141 }; 142 142 EXPORT_SYMBOL_NS_GPL(ssd130x_variants, DRM_SSD130X); 143 143 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 + 144 157 static inline struct ssd130x_device *drm_to_ssd130x(struct drm_device *drm) 145 158 { 146 159 return container_of(drm, struct ssd130x_device, drm); ··· 447 434 SSD130X_SET_ADDRESS_MODE_HORIZONTAL); 448 435 } 449 436 450 - static int ssd130x_update_rect(struct ssd130x_device *ssd130x, struct drm_rect *rect) 437 + static int ssd130x_update_rect(struct ssd130x_device *ssd130x, 438 + struct ssd130x_plane_state *ssd130x_state, 439 + struct drm_rect *rect) 451 440 { 452 441 unsigned int x = rect->x1; 453 442 unsigned int y = rect->y1; 454 - u8 *buf = ssd130x->buffer; 455 - u8 *data_array = ssd130x->data_array; 443 + u8 *buf = ssd130x_state->buffer; 444 + u8 *data_array = ssd130x_state->data_array; 456 445 unsigned int width = drm_rect_width(rect); 457 446 unsigned int height = drm_rect_height(rect); 458 447 unsigned int line_length = DIV_ROUND_UP(width, 8); ··· 550 535 return ret; 551 536 } 552 537 553 - static void ssd130x_clear_screen(struct ssd130x_device *ssd130x) 538 + static void ssd130x_clear_screen(struct ssd130x_device *ssd130x, 539 + struct ssd130x_plane_state *ssd130x_state) 554 540 { 555 541 struct drm_rect fullscreen = { 556 542 .x1 = 0, ··· 560 544 .y2 = ssd130x->height, 561 545 }; 562 546 563 - ssd130x_update_rect(ssd130x, &fullscreen); 547 + ssd130x_update_rect(ssd130x, ssd130x_state, &fullscreen); 564 548 } 565 549 566 - static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb, const struct iosys_map *vmap, 550 + static int ssd130x_fb_blit_rect(struct drm_plane_state *state, 551 + const struct iosys_map *vmap, 567 552 struct drm_rect *rect) 568 553 { 554 + struct drm_framebuffer *fb = state->fb; 569 555 struct ssd130x_device *ssd130x = drm_to_ssd130x(fb->dev); 570 556 unsigned int page_height = ssd130x->device_info->page_height; 557 + struct ssd130x_plane_state *ssd130x_state = to_ssd130x_plane_state(state); 558 + u8 *buf = ssd130x_state->buffer; 571 559 struct iosys_map dst; 572 560 unsigned int dst_pitch; 573 561 int ret = 0; 574 - u8 *buf = ssd130x->buffer; 575 - 576 - if (!buf) 577 - return 0; 578 562 579 563 /* Align y to display page boundaries */ 580 564 rect->y1 = round_down(rect->y1, page_height); ··· 591 575 592 576 drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); 593 577 594 - ssd130x_update_rect(ssd130x, rect); 578 + ssd130x_update_rect(ssd130x, ssd130x_state, rect); 595 579 596 580 return ret; 581 + } 582 + 583 + static int ssd130x_primary_plane_helper_atomic_check(struct drm_plane *plane, 584 + struct drm_atomic_state *state) 585 + { 586 + struct drm_device *drm = plane->dev; 587 + struct ssd130x_device *ssd130x = drm_to_ssd130x(drm); 588 + struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); 589 + struct ssd130x_plane_state *ssd130x_state = to_ssd130x_plane_state(plane_state); 590 + unsigned int page_height = ssd130x->device_info->page_height; 591 + unsigned int pages = DIV_ROUND_UP(ssd130x->height, page_height); 592 + const struct drm_format_info *fi; 593 + unsigned int pitch; 594 + int ret; 595 + 596 + ret = drm_plane_helper_atomic_check(plane, state); 597 + if (ret) 598 + return ret; 599 + 600 + fi = drm_format_info(DRM_FORMAT_R1); 601 + if (!fi) 602 + return -EINVAL; 603 + 604 + pitch = drm_format_info_min_pitch(fi, 0, ssd130x->width); 605 + 606 + ssd130x_state->buffer = kcalloc(pitch, ssd130x->height, GFP_KERNEL); 607 + if (!ssd130x_state->buffer) 608 + return -ENOMEM; 609 + 610 + ssd130x_state->data_array = kcalloc(ssd130x->width, pages, GFP_KERNEL); 611 + if (!ssd130x_state->data_array) { 612 + kfree(ssd130x_state->buffer); 613 + /* Set to prevent a double free in .atomic_destroy_state() */ 614 + ssd130x_state->buffer = NULL; 615 + return -ENOMEM; 616 + } 617 + 618 + return 0; 597 619 } 598 620 599 621 static void ssd130x_primary_plane_helper_atomic_update(struct drm_plane *plane, ··· 656 602 if (!drm_rect_intersect(&dst_clip, &damage)) 657 603 continue; 658 604 659 - ssd130x_fb_blit_rect(plane_state->fb, &shadow_plane_state->data[0], &dst_clip); 605 + ssd130x_fb_blit_rect(plane_state, &shadow_plane_state->data[0], &dst_clip); 660 606 } 661 607 662 608 drm_dev_exit(idx); ··· 667 613 { 668 614 struct drm_device *drm = plane->dev; 669 615 struct ssd130x_device *ssd130x = drm_to_ssd130x(drm); 616 + struct ssd130x_plane_state *ssd130x_state = to_ssd130x_plane_state(plane->state); 670 617 int idx; 671 618 672 619 if (!drm_dev_enter(drm, &idx)) 673 620 return; 674 621 675 - ssd130x_clear_screen(ssd130x); 622 + ssd130x_clear_screen(ssd130x, ssd130x_state); 676 623 677 624 drm_dev_exit(idx); 678 625 } 679 626 627 + /* Called during init to allocate the plane's atomic state. */ 628 + static void ssd130x_primary_plane_reset(struct drm_plane *plane) 629 + { 630 + struct ssd130x_plane_state *ssd130x_state; 631 + 632 + WARN_ON(plane->state); 633 + 634 + ssd130x_state = kzalloc(sizeof(*ssd130x_state), GFP_KERNEL); 635 + if (!ssd130x_state) 636 + return; 637 + 638 + __drm_atomic_helper_plane_reset(plane, &ssd130x_state->base); 639 + } 640 + 641 + static struct drm_plane_state *ssd130x_primary_plane_duplicate_state(struct drm_plane *plane) 642 + { 643 + struct ssd130x_plane_state *old_ssd130x_state; 644 + struct ssd130x_plane_state *ssd130x_state; 645 + 646 + if (WARN_ON(!plane->state)) 647 + return NULL; 648 + 649 + old_ssd130x_state = to_ssd130x_plane_state(plane->state); 650 + ssd130x_state = kmemdup(old_ssd130x_state, sizeof(*ssd130x_state), GFP_KERNEL); 651 + if (!ssd130x_state) 652 + return NULL; 653 + 654 + /* The buffers are not duplicated and are allocated in .atomic_check */ 655 + ssd130x_state->buffer = NULL; 656 + ssd130x_state->data_array = NULL; 657 + 658 + __drm_atomic_helper_plane_duplicate_state(plane, &ssd130x_state->base); 659 + 660 + return &ssd130x_state->base; 661 + } 662 + 663 + static void ssd130x_primary_plane_destroy_state(struct drm_plane *plane, 664 + struct drm_plane_state *state) 665 + { 666 + struct ssd130x_plane_state *ssd130x_state = to_ssd130x_plane_state(state); 667 + 668 + kfree(ssd130x_state->data_array); 669 + kfree(ssd130x_state->buffer); 670 + 671 + __drm_atomic_helper_plane_destroy_state(&ssd130x_state->base); 672 + 673 + kfree(ssd130x_state); 674 + } 675 + 680 676 static const struct drm_plane_helper_funcs ssd130x_primary_plane_helper_funcs = { 681 677 DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, 682 - .atomic_check = drm_plane_helper_atomic_check, 678 + .atomic_check = ssd130x_primary_plane_helper_atomic_check, 683 679 .atomic_update = ssd130x_primary_plane_helper_atomic_update, 684 680 .atomic_disable = ssd130x_primary_plane_helper_atomic_disable, 685 681 }; ··· 737 633 static const struct drm_plane_funcs ssd130x_primary_plane_funcs = { 738 634 .update_plane = drm_atomic_helper_update_plane, 739 635 .disable_plane = drm_atomic_helper_disable_plane, 636 + .reset = ssd130x_primary_plane_reset, 637 + .atomic_duplicate_state = ssd130x_primary_plane_duplicate_state, 638 + .atomic_destroy_state = ssd130x_primary_plane_destroy_state, 740 639 .destroy = drm_plane_cleanup, 741 640 DRM_GEM_SHADOW_PLANE_FUNCS, 742 641 }; ··· 784 677 { 785 678 struct drm_device *drm = encoder->dev; 786 679 struct ssd130x_device *ssd130x = drm_to_ssd130x(drm); 787 - unsigned int page_height = ssd130x->device_info->page_height; 788 - unsigned int pages = DIV_ROUND_UP(ssd130x->height, page_height); 789 - const struct drm_format_info *fi; 790 - unsigned int pitch; 791 680 int ret; 792 681 793 682 ret = ssd130x_power_on(ssd130x); ··· 793 690 ret = ssd130x_init(ssd130x); 794 691 if (ret) 795 692 goto power_off; 796 - 797 - fi = drm_format_info(DRM_FORMAT_R1); 798 - if (!fi) 799 - goto power_off; 800 - 801 - pitch = drm_format_info_min_pitch(fi, 0, ssd130x->width); 802 - 803 - ssd130x->buffer = kcalloc(pitch, ssd130x->height, GFP_KERNEL); 804 - if (!ssd130x->buffer) 805 - goto power_off; 806 - 807 - ssd130x->data_array = kcalloc(ssd130x->width, pages, GFP_KERNEL); 808 - if (!ssd130x->data_array) { 809 - kfree(ssd130x->buffer); 810 - goto power_off; 811 - } 812 693 813 694 ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_ON); 814 695 ··· 814 727 backlight_disable(ssd130x->bl_dev); 815 728 816 729 ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_OFF); 817 - 818 - kfree(ssd130x->data_array); 819 - kfree(ssd130x->buffer); 820 730 821 731 ssd130x_power_off(ssd130x); 822 732 }
-3
drivers/gpu/drm/solomon/ssd130x.h
··· 89 89 u8 col_end; 90 90 u8 page_start; 91 91 u8 page_end; 92 - 93 - u8 *buffer; 94 - u8 *data_array; 95 92 }; 96 93 97 94 extern const struct ssd130x_deviceinfo ssd130x_variants[];