From: "Noralf Trønnes" <noralf@tronnes.org>
To: Daniel Vetter <daniel@ffwll.ch>
Cc: thomas.petazzoni@free-electrons.com, dri-devel@lists.freedesktop.org
Subject: Re: [RFC 1/5] drm: Add DRM support for tiny LCD displays
Date: Fri, 25 Mar 2016 11:41:44 +0100 [thread overview]
Message-ID: <56F515E8.3040408@tronnes.org> (raw)
In-Reply-To: <20160323172825.GD2510@phenom.ffwll.local>
Den 23.03.2016 18:28, skrev Daniel Vetter:
> On Wed, Mar 23, 2016 at 06:07:56PM +0100, Noralf Trønnes wrote:
>> Den 18.03.2016 18:47, skrev Daniel Vetter:
>>> On Thu, Mar 17, 2016 at 10:51:55PM +0100, Noralf Trønnes wrote:
>>>> Den 16.03.2016 16:11, skrev Daniel Vetter:
>>>>> On Wed, Mar 16, 2016 at 02:34:15PM +0100, Noralf Trønnes wrote:
>>>>>> tinydrm provides a very simplified view of DRM for displays that has
>>>>>> onboard video memory and is connected through a slow bus like SPI/I2C.
>>>>>>
>>>>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
>>>>> Yay, it finally happens! I already made a comment on the cover letter
>>>>> about the fbdev stuff, I think that's the biggest part to split out from
>>>>> tinydrm here. I'm not entirely sure a detailed code review makes sense
>>>>> before that part is done (and hey we can start merging already), so just a
>>>>> high level review for now:
>> [...]
>>>
>>>>> In the case of tinydrm I think that means we should have a bunch of new
>>>>> drm helpers, or extensions for existing ones:
>>>>> - fbdev deferred io support using ->dirtyfb in drm_fb_helper.c.
>>>> Are you thinking something like this?
>>>>
>>>> struct drm_fb_helper_funcs {
>>>> int (*dirtyfb)(struct drm_fb_helper *fb_helper,
>>>> struct drm_clip_rect *clip);
>>> We already have a dirty_fb function in
>>> dev->mode_config->funcs->dirty_fb(). This is the official interface native
>>> drm/kms userspace is supposed to use to flush frontbuffer rendering. The
>>> xfree86-video-modesetting driver uses it.
>> I couldn't find this dirty_fb() function, but I assume you mean
>> drm_framebuffer_funcs.dirty().
> Yup.
>
>>>> };
>>>>
>>>> struct drm_fb_helper {
>>>> spinlock_t dirty_lock;
>>>> struct drm_clip_rect *dirty_clip;
>>>> };
>>> Yeah, this part is needed for the delayed work for the fbdev helper.
>>> struct work dirty_fb_work; is missing.
>> This confuses me.
>> If we have this then there's no need for a fb->funcs->dirty() call,
>> the driver can just add a work function here instead.
>>
>> Possible fb dirty() call chain:
>> Calls to drm_fb_helper_sys_* or mmap page writes will schedule
>> fb_info->deferred_work. The worker func fb_deferred_io_work() calls
>> fb_info->fbdefio->deferred_io().
>> Then deferred_io() can call fb_helper->fb->funcs->dirty().
>>
>> In my use-case this dirty() function would schedule a delayed_work to run
>> immediately since it has already been deferred collecting changes.
>> The regular drm side framebuffer dirty() collects damage and schedules
>> the same worker to run deferred.
>>
>> I don't see an easy way for a driver to set the dirty() function in
>> drm_fb_cma_helper apart from doing this:
>>
>> struct drm_fbdev_cma {
>> struct drm_fb_helper fb_helper;
>> struct drm_fb_cma *fb;
>> + int (*dirty)(struct drm_framebuffer *framebuffer,
>> + struct drm_file *file_priv, unsigned flags,
>> + unsigned color, struct drm_clip_rect *clips,
>> + unsigned num_clips);
>> };
> Well my point is that drm core already has a canonical interface
> (drm_framebuffer_funcs.dirty) to flush out rendering. And it's supposed to
> be called from process context, and userspace is supposed to batch up
> dirty updates.
Batched up into one ioctl call?
If that's the case, then I don't have to use delayed_work like I do now.
I can just queue a work_struct to run immediately.
This comment in include/uapi/drm/drm_mode.h made me think that I might
receive multiple calls:
* The kernel or hardware is free to update more then just the
* region specified by the clip rects. The kernel or hardware
* may also delay and/or coalesce several calls to dirty into a
* single update.
I have assumed that I can't run the whole display update from the ioctl
call since one full display update on the "worst" display takes ~200ms.
But maybe it's fine to run all this in the ioctl call?
> What I'd like is that the fbdev emulation uses exactly that interface,
> without requiring drivers to write any additional fbdev code (like qxl and
> udl currently have). Since the drm_framebuffer_funcs.dirty is already
> expected to run in process context I think the only bit we need is the
> deferred_work you already added in fbdev, so that we can schedule the
> driver's ->dirty() function.
>
> There shouldn't be any need to have another ->dirty() function anywhere
> else.
I'll try and see if I can explain better with some code.
First the drm_fb_helper part:
void drm_fb_helper_deferred_io(struct fb_info *info,
struct list_head *pagelist)
{
struct drm_fb_helper *helper = info->par;
unsigned long start, end, min, max;
struct drm_clip_rect clip;
struct page *page;
if (!helper->fb->funcs->dirty)
return;
spin_lock(&helper->dirty_lock);
clip = *helper->dirty_clip;
/* reset dirty_clip */
spin_unlock(&helper->dirty_lock);
min = ULONG_MAX;
max = 0;
list_for_each_entry(page, pagelist, lru) {
start = page->index << PAGE_SHIFT;
end = start + PAGE_SIZE - 1;
min = min(min, start);
max = max(max, end);
}
if (min < max) {
/* TODO merge with clip */
}
helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, &clip, 1);
}
EXPORT_SYMBOL(drm_fb_helper_deferred_io);
/* Called by the drm_fb_helper_sys_*() functions */
static void drm_fb_helper_sys_deferred(struct fb_info *info, u32 x, u32 y,
u32 width, u32 height)
{
struct drm_fb_helper *helper = info->par;
struct drm_clip_rect clip;
if (!info->fbdefio)
return;
clip.x1 = x;
clip.x2 = x + width -1;
cliy.y1 = y;
clip.y2 = y + height - 1;
spin_lock(&helper->dirty_lock);
/* TODO merge clip with helper->dirty_clip */
spin_unlock(&helper->dirty_lock);
schedule_delayed_work(&info->deferred_work, info->fbdefio->delay);
}
So the question I have asked is this: How can the driver set the
dirty() function within drm_fb_cma_helper?
Having looked at the code over and over again, I have a suggestion,
but it assumes that it's allowed to change fb->funcs.
First the necessary drm_fb_cma_helper changes:
EXPORT_SYMBOL(drm_fb_cma_destroy);
EXPORT_SYMBOL(drm_fb_cma_create_handle);
EXPORT_SYMBOL(drm_fbdev_cma_create);
/* This is the drm_fbdev_cma_init() code with one change */
struct drm_fbdev_cma *drm_fbdev_cma_init_with_funcs(struct drm_device *dev,
unsigned int preferred_bpp, unsigned int num_crtc,
unsigned int max_conn_count, struct drm_framebuffer_funcs *funcs)
{
[...]
drm_fb_helper_prepare(dev, helper, funcs);
[...]
}
struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
unsigned int preferred_bpp, unsigned int num_crtc,
unsigned int max_conn_count)
{
return drm_fbdev_cma_init_with_funcs(dev, preferred_bpp, num_crtc,
max_conn_count,
&drm_fb_cma_helper_funcs);
}
Now tinydrm should be able to do this:
static int tinydrm_fbdev_dirty(struct drm_framebuffer *fb,
struct drm_file *file_priv,
unsigned flags, unsigned color,
struct drm_clip_rect *clips,
unsigned num_clips)
{
struct drm_fb_helper *helper = info->par;
struct tinydrm_device *tdev = helper->dev->dev_private;
struct drm_framebuffer *fb = helper->fb;
struct drm_gem_cma_object *cma_obj;
if (tdev->plane.fb != fb)
return 0;
cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
if (!cma_obj) {
dev_err_once(info->dev, "Can't get cma_obj\n");
return -EINVAL;
}
return tdev->dirtyfb(fb, cma_obj->vaddr, flags, color, clips,
num_clips);
}
static struct drm_framebuffer_funcs tinydrm_fb_cma_funcs = {
.destroy = drm_fb_cma_destroy,
.create_handle = drm_fb_cma_create_handle,
.dirty = tinydrm_fbdev_dirty,
};
static int tinydrm_fbdev_create(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
struct tinydrm_device *tdev = helper->dev->dev_private;
struct fb_deferred_io *fbdefio;
struct fb_ops *fbops;
struct fb_info *info;
/*
* A per device structure is needed for:
* - fbops because fb_deferred_io_cleanup() clears fbops.fb_mmap
* - fbdefio to get per device delay
*/
fbops = devm_kzalloc(helper->dev->dev, sizeof(*fbops), GFP_KERNEL);
fbdefio = devm_kzalloc(helper->dev->dev, sizeof(*fbdefio),
GFP_KERNEL);
if (!fbops || !fbdefio)
return -ENOMEM;
ret = drm_fbdev_cma_create(helper, sizes);
if (ret)
return ret;
helper->fb->funcs = &tinydrm_fb_cma_funcs;
info = fb_helper->fbdev;
/*
* fb_deferred_io requires a vmalloc address or a physical address.
* drm_fbdev_cma_create() sets smem_start to the dma address
which is
* a device address. This isn't always a physical address.
*/
info->smem_start = page_to_phys(virt_to_page(info->screen_buffer));
*fbops = *info->fbops;
info->fbops = fbops;
info->fbdefio = fbdefio;
fbdefio->deferred_io = drm_fb_helper_deferred_io;
fbdefio->delay = msecs_to_jiffies(tdev->deferred->defer_ms);
/* delay=0 is turned into delay=HZ, so use 1 as a minimum */
if (!fbdefio->delay)
fbdefio->delay = 1;
fb_deferred_io_init(info);
return 0;
}
static const struct drm_fb_helper_funcs tinydrm_fb_helper_funcs = {
.fb_probe = tinydrm_fbdev_create,
};
int tinydrm_fbdev_init(struct tinydrm_device *tdev)
{
struct drm_device *dev = tdev->base;
struct drm_fbdev_cma *cma;
cma = drm_fbdev_cma_init_with_funcs(dev, 16,
dev->mode_config.num_crtc,
dev->mode_config.num_connector,
&tinydrm_fb_helper_funcs);
if (IS_ERR(cma))
return PTR_ERR(cma);
tdev->fbdev_cma = cma;
return 0;
}
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
next prev parent reply other threads:[~2016-03-25 10:42 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-03-16 13:34 [RFC 0/5] drm: Add support for tiny LCD displays Noralf Trønnes
2016-03-16 13:34 ` [RFC 1/5] drm: Add DRM " Noralf Trønnes
2016-03-16 15:11 ` Daniel Vetter
2016-03-17 21:51 ` Noralf Trønnes
2016-03-18 17:47 ` Daniel Vetter
2016-03-23 17:07 ` Noralf Trønnes
2016-03-23 17:28 ` Daniel Vetter
2016-03-25 10:41 ` Noralf Trønnes [this message]
2016-03-29 7:27 ` Daniel Vetter
2016-04-01 19:15 ` Noralf Trønnes
2016-04-12 10:40 ` Daniel Vetter
2016-03-23 17:37 ` David Herrmann
2016-03-25 19:39 ` Noralf Trønnes
2016-03-16 13:34 ` [RFC 2/5] drm/tinydrm: Add lcd register abstraction Noralf Trønnes
2016-03-16 13:34 ` [RFC 3/5] drm/tinydrm/lcdreg: Add SPI support Noralf Trønnes
2016-03-16 13:34 ` [RFC 4/5] drm/tinydrm: Add mipi-dbi support Noralf Trønnes
2016-03-16 13:34 ` [RFC 5/5] drm/tinydrm: Add support for several Adafruit TFT displays Noralf Trønnes
2016-03-16 14:50 ` [RFC 0/5] drm: Add support for tiny LCD displays Daniel Vetter
2016-03-16 18:26 ` Eric Anholt
2016-03-17 22:00 ` Noralf Trønnes
2016-03-18 17:48 ` Daniel Vetter
2016-03-26 19:11 ` Noralf Trønnes
2016-03-29 7:29 ` Daniel Vetter
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=56F515E8.3040408@tronnes.org \
--to=noralf@tronnes.org \
--cc=daniel@ffwll.ch \
--cc=dri-devel@lists.freedesktop.org \
--cc=thomas.petazzoni@free-electrons.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox