On Thu, Feb 22, 2018 at 09:06:52PM +0100, Noralf Trønnes wrote: > Just a hack to test the client API. > > Signed-off-by: Noralf Trønnes <noralf@xxxxxxxxxxx> Adding the suse folks who submitted the bootsplash a while ago, would be great if they could pick this up and run with it. > --- > drivers/gpu/drm/client/Kconfig | 5 + > drivers/gpu/drm/client/Makefile | 1 + > drivers/gpu/drm/client/drm_bootsplash.c | 205 ++++++++++++++++++++++++++++++++ > 3 files changed, 211 insertions(+) > create mode 100644 drivers/gpu/drm/client/drm_bootsplash.c > > diff --git a/drivers/gpu/drm/client/Kconfig b/drivers/gpu/drm/client/Kconfig > index 73902ab44c75..16cf1e14620a 100644 > --- a/drivers/gpu/drm/client/Kconfig > +++ b/drivers/gpu/drm/client/Kconfig > @@ -17,4 +17,9 @@ config DRM_CLIENT_FBDEV > help > Generic fbdev emulation > > +config DRM_CLIENT_BOOTSPLASH > + tristate "DRM Bootsplash" > + help > + DRM Bootsplash > + > endmenu > diff --git a/drivers/gpu/drm/client/Makefile b/drivers/gpu/drm/client/Makefile > index 3ff694429dec..8660530e4646 100644 > --- a/drivers/gpu/drm/client/Makefile > +++ b/drivers/gpu/drm/client/Makefile > @@ -1,3 +1,4 @@ > # SPDX-License-Identifier: GPL-2.0 > > obj-$(CONFIG_DRM_CLIENT_FBDEV) += drm_fbdev.o > +obj-$(CONFIG_DRM_CLIENT_BOOTSPLASH) += drm_bootsplash.o > diff --git a/drivers/gpu/drm/client/drm_bootsplash.c b/drivers/gpu/drm/client/drm_bootsplash.c > new file mode 100644 > index 000000000000..43c703606e74 > --- /dev/null > +++ b/drivers/gpu/drm/client/drm_bootsplash.c > @@ -0,0 +1,205 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +#include <linux/delay.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/slab.h> > +#include <linux/workqueue.h> > + > +#include <drm/drm_client.h> > +#include <drm/drm_drv.h> > +#include <drm/drm_fourcc.h> > +#include <drm/drm_modes.h> > +#include <drm/drm_print.h> > + > +struct drm_bootsplash { > + struct drm_client_dev *client; > + struct drm_client_display *display; > + struct drm_client_buffer *buffer[2]; > + struct work_struct worker; > + bool stop; > +}; > + > +static u32 drm_bootsplash_color_table[3] = { > + 0x00ff0000, 0x0000ff00, 0x000000ff, > +}; > + > +/* Draw a box with changing colors */ > +static void > +drm_bootsplash_draw(struct drm_client_buffer *buffer, unsigned int sequence) > +{ > + unsigned int x, y; > + u32 *pix; > + > + pix = buffer->vaddr; > + pix += ((buffer->height / 2) - 50) * buffer->width; > + pix += (buffer->width / 2) - 50; > + > + for (y = 0; y < 100; y++) { > + for (x = 0; x < 100; x++) > + *pix++ = drm_bootsplash_color_table[sequence]; > + pix += buffer->width - 100; > + } > +} > + > +static void drm_bootsplash_worker(struct work_struct *work) > +{ > + struct drm_bootsplash *splash = container_of(work, struct drm_bootsplash, > + worker); > + struct drm_event *event; > + unsigned int i = 0, sequence = 0, fb_id; > + int ret; > + > + while (!splash->stop) { > + /* Are we still in charge? */ > + fb_id = drm_client_display_current_fb(splash->display); > + if (fb_id != splash->buffer[i]->fb_ids[0]) > + break; > + > + /* > + * We can race with userspace here between checking and doing > + * the page flip, so double buffering isn't such a good idea. > + * Tearing probably isn't a problem on a presumably small splash > + * animation. I've kept it to test the page flip code. > + */ I think a much cleaner way to solve all this is to tie it into our master tracking. If there is a master (even if it's not displaying anything), then none of the in-kernel clients should display anything. If there is not, then we grab a temporary/weak master reference which blocks other masters for the time being, but only until we've complete our drawing operation. Then the in-kernel client drops that weak reference again. A very simply solution would be to simply hold the device's master lock (but I'm not sure whether that would result in deadlocks). That would mean something like if (!drm_master_try_internal_acquire()) /* someone else is master */ break; here instead of your fb id check. > + > + i = !i; > + drm_bootsplash_draw(splash->buffer[i], sequence++); > + if (sequence == 3) > + sequence = 0; > + > + ret = drm_client_display_page_flip(splash->display, > + splash->buffer[i]->fb_ids[0], > + true); > + if (!ret) { > + event = drm_client_read_event(splash->client, true); > + if (!IS_ERR(event)) > + kfree(event); > + } And drm_master_interal_relase() here before we sleep again. Similar for all the fbdev ioctls and all that stuff. Just an idea, names definitely need improvements, and I have no idea wheter it'll work. But using the same logic in the existing fbdev emulation code would be really good (since that's used a lot more, at least right now). -Daniel > + msleep(500); > + } > + > + for (i = 0; i < 2; i++) > + drm_client_framebuffer_delete(splash->buffer[i]); > + drm_client_display_free(splash->display); > +} > + > +static int drm_bootsplash_setup(struct drm_bootsplash *splash) > +{ > + struct drm_client_dev *client = splash->client; > + struct drm_client_buffer *buffer[2]; > + struct drm_client_display *display; > + struct drm_mode_modeinfo *mode; > + int ret, i; > + > + display = drm_client_display_get_first_enabled(client, false); > + if (IS_ERR(display)) > + return PTR_ERR(display); > + if (!display) > + return -ENOENT; > + > + mode = drm_client_display_first_mode(display); > + if (!mode) { > + ret = -EINVAL; > + goto err_free_display; > + } > + > + for (i = 0; i < 2; i++) { > + buffer[i] = drm_client_framebuffer_create(client, mode, > + DRM_FORMAT_XRGB8888); > + if (IS_ERR(buffer[i])) { > + ret = PTR_ERR(buffer[i]); > + goto err_free_buffer; > + } > + } > + > + ret = drm_client_display_commit_mode(display, buffer[0]->fb_ids[0], mode); > + if (ret) > + goto err_free_buffer; > + > + splash->display = display; > + splash->buffer[0] = buffer[0]; > + splash->buffer[1] = buffer[1]; > + > + schedule_work(&splash->worker); > + > + return 0; > + > +err_free_buffer: > + for (i--; i >= 0; i--) > + drm_client_framebuffer_delete(buffer[i]); > +err_free_display: > + drm_client_display_free(display); > + > + return ret; > +} > + > +static int drm_bootsplash_client_hotplug(struct drm_client_dev *client) > +{ > + struct drm_bootsplash *splash = client->private; > + int ret = 0; > + > + if (!splash->display) > + ret = drm_bootsplash_setup(splash); > + > + return ret; > +} > + > +static int drm_bootsplash_client_new(struct drm_client_dev *client) > +{ > + struct drm_bootsplash *splash; > + > + splash = kzalloc(sizeof(*splash), GFP_KERNEL); > + if (!splash) > + return -ENOMEM; > + > + INIT_WORK(&splash->worker, drm_bootsplash_worker); > + > + splash->client = client; > + client->private = splash; > + > + /* > + * vc4 isn't done with it's setup when drm_dev_register() is called. > + * It should have shouldn't it? > + * So to keep it from crashing defer setup to hotplug... > + */ > + if (client->dev->mode_config.max_width) > + drm_bootsplash_client_hotplug(client); > + > + return 0; > +} > + > +static int drm_bootsplash_client_remove(struct drm_client_dev *client) > +{ > + struct drm_bootsplash *splash = client->private; > + > + if (splash->display) { > + splash->stop = true; > + flush_work(&splash->worker); > + } > + > + kfree(splash); > + > + return 0; > +} > + > +static const struct drm_client_funcs drm_bootsplash_client_funcs = { > + .name = "drm_bootsplash", > + .new = drm_bootsplash_client_new, > + .remove = drm_bootsplash_client_remove, > + .hotplug = drm_bootsplash_client_hotplug, > +}; > + > +static int __init drm_bootsplash_init(void) > +{ > + return drm_client_register(&drm_bootsplash_client_funcs); > +} > +module_init(drm_bootsplash_init); > + > +static void __exit drm_bootsplash_exit(void) > +{ > + drm_client_unregister(&drm_bootsplash_client_funcs); > +} > +module_exit(drm_bootsplash_exit); > + > +MODULE_LICENSE("GPL"); > -- > 2.15.1 > -- Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel