From: Greg Hackmann Date: Thu, 10 Oct 2013 20:03:26 +0000 (-0700) Subject: video: adf: support "simple" buffers X-Git-Tag: firefly_0821_release~2958^2~235 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=68ff076002b28bbc34dff95048aa815062996b74;p=firefly-linux-kernel-4.4.55.git video: adf: support "simple" buffers Simple buffers are linear RGB buffers analogous to KMS's dumb buffers. Simple buffers can be allocated and posted to a display interface without any driver-private data. Internally, ADF drivers provide the driver-private data needed (if any) to post a simple buffer to the display. Change-Id: Ib0b737622eaf343111310f6623f99d69cf3807d2 Signed-off-by: Greg Hackmann --- diff --git a/drivers/video/adf/adf_client.c b/drivers/video/adf/adf_client.c index f52b29d1a9a4..48e97ef262fb 100644 --- a/drivers/video/adf/adf_client.c +++ b/drivers/video/adf/adf_client.c @@ -762,3 +762,81 @@ done: return ret; } EXPORT_SYMBOL(adf_device_detach); + +/** + * adf_interface_simple_buffer_alloc - allocate a simple buffer + * + * @intf: target interface + * @w: width in pixels + * @h: height in pixels + * @format: format fourcc + * @dma_buf: returns the allocated buffer + * @offset: returns the byte offset of the allocated buffer's first pixel + * @pitch: returns the allocated buffer's pitch + * + * See &struct adf_simple_buffer_alloc for a description of simple buffers and + * their limitations. + * + * Returns 0 on success or -errno on failure. + */ +int adf_interface_simple_buffer_alloc(struct adf_interface *intf, u16 w, u16 h, + u32 format, struct dma_buf **dma_buf, u32 *offset, u32 *pitch) +{ + if (!intf->ops || !intf->ops->alloc_simple_buffer) + return -EOPNOTSUPP; + + if (!adf_format_is_rgb(format)) + return -EINVAL; + + return intf->ops->alloc_simple_buffer(intf, w, h, format, dma_buf, + offset, pitch); +} +EXPORT_SYMBOL(adf_interface_simple_buffer_alloc); + +/** + * adf_interface_simple_post - flip to a single buffer + * + * @intf: interface targeted by the flip + * @buf: buffer to display + * + * adf_interface_simple_post() can be used generically for simple display + * configurations, since the client does not need to provide any driver-private + * configuration data. + * + * adf_interface_simple_post() has the same copying semantics as + * adf_device_post(). + * + * On success, returns a sync fence which signals when the buffer is removed + * from the screen. On failure, returns ERR_PTR(-errno). + */ +struct sync_fence *adf_interface_simple_post(struct adf_interface *intf, + struct adf_buffer *buf) +{ + size_t custom_data_size = 0; + void *custom_data = NULL; + struct sync_fence *ret; + + if (intf->ops && intf->ops->describe_simple_post) { + int err; + + custom_data = kzalloc(ADF_MAX_CUSTOM_DATA_SIZE, GFP_KERNEL); + if (!custom_data) { + ret = ERR_PTR(-ENOMEM); + goto done; + } + + err = intf->ops->describe_simple_post(intf, buf, custom_data, + &custom_data_size); + if (err < 0) { + ret = ERR_PTR(err); + goto done; + } + } + + ret = adf_device_post(adf_interface_parent(intf), &intf, 1, buf, 1, + custom_data, custom_data_size); +done: + kfree(custom_data); + return ret; +} +EXPORT_SYMBOL(adf_interface_simple_post); diff --git a/drivers/video/adf/adf_fops.c b/drivers/video/adf/adf_fops.c index 90b234a76662..d2e30c9cddcf 100644 --- a/drivers/video/adf/adf_fops.c +++ b/drivers/video/adf/adf_fops.c @@ -310,6 +310,79 @@ err_get_user: return ret; } +static int adf_intf_simple_post_config(struct adf_interface *intf, + struct adf_simple_post_config __user *arg) +{ + struct adf_device *dev = intf->base.parent; + struct sync_fence *complete_fence; + int complete_fence_fd; + struct adf_buffer buf; + int ret = 0; + + complete_fence_fd = get_unused_fd(); + if (complete_fence_fd < 0) + return complete_fence_fd; + + ret = adf_buffer_import(dev, &arg->buf, &buf); + if (ret < 0) + goto err_import; + + if (put_user(complete_fence_fd, &arg->complete_fence)) { + ret = -EFAULT; + goto err_put_user; + } + + complete_fence = adf_interface_simple_post(intf, &buf); + if (IS_ERR(complete_fence)) { + ret = PTR_ERR(complete_fence); + goto err_put_user; + } + + sync_fence_install(complete_fence, complete_fence_fd); + return 0; + +err_put_user: + adf_buffer_cleanup(&buf); +err_import: + put_unused_fd(complete_fence_fd); + return ret; +} + +static int adf_intf_simple_buffer_alloc(struct adf_interface *intf, + struct adf_simple_buffer_alloc __user *arg) +{ + struct adf_simple_buffer_alloc data; + struct dma_buf *dma_buf; + int ret = 0; + + if (copy_from_user(&data, arg, sizeof(data))) + return -EFAULT; + + data.fd = get_unused_fd_flags(O_CLOEXEC); + if (data.fd < 0) + return data.fd; + + ret = adf_interface_simple_buffer_alloc(intf, data.w, data.h, + data.format, &dma_buf, &data.offset, &data.pitch); + if (ret < 0) + goto err_alloc; + + if (copy_to_user(arg, &data, sizeof(*arg))) { + ret = -EFAULT; + goto err_copy; + } + + fd_install(data.fd, dma_buf->file); + return 0; + +err_copy: + dma_buf_put(dma_buf); + +err_alloc: + put_unused_fd(data.fd); + return ret; +} + static int adf_copy_attachment_list_to_user( struct adf_attachment_config __user *to, size_t n_to, struct adf_attachment *from, size_t n_from) @@ -539,6 +612,8 @@ static long adf_overlay_engine_ioctl(struct adf_overlay_engine *eng, case ADF_SET_MODE: case ADF_GET_DEVICE_DATA: case ADF_GET_INTERFACE_DATA: + case ADF_SIMPLE_POST_CONFIG: + case ADF_SIMPLE_BUFFER_ALLOC: case ADF_ATTACH: case ADF_DETACH: return -EINVAL; @@ -567,6 +642,14 @@ static long adf_interface_ioctl(struct adf_interface *intf, return adf_intf_get_data(intf, (struct adf_interface_data __user *)arg); + case ADF_SIMPLE_POST_CONFIG: + return adf_intf_simple_post_config(intf, + (struct adf_simple_post_config __user *)arg); + + case ADF_SIMPLE_BUFFER_ALLOC: + return adf_intf_simple_buffer_alloc(intf, + (struct adf_simple_buffer_alloc __user *)arg); + case ADF_POST_CONFIG: case ADF_GET_DEVICE_DATA: case ADF_GET_OVERLAY_ENGINE_DATA: @@ -609,6 +692,8 @@ static long adf_device_ioctl(struct adf_device *dev, struct adf_file *file, case ADF_SET_MODE: case ADF_GET_INTERFACE_DATA: case ADF_GET_OVERLAY_ENGINE_DATA: + case ADF_SIMPLE_POST_CONFIG: + case ADF_SIMPLE_BUFFER_ALLOC: return -EINVAL; default: diff --git a/include/uapi/video/adf.h b/include/uapi/video/adf.h index ec6508a5d372..bd046c6c3409 100644 --- a/include/uapi/video/adf.h +++ b/include/uapi/video/adf.h @@ -147,11 +147,54 @@ struct adf_post_config { size_t custom_data_size; void __user *custom_data; - __s64 complete_fence; }; #define ADF_MAX_INTERFACES (PAGE_SIZE / sizeof(__u32)) +/** + * struct adf_simple_buffer_allocate - request to allocate a "simple" buffer + * + * @w: width of buffer in pixels (input) + * @h: height of buffer in pixels (input) + * @format: DRM-style fourcc (input) + * + * @fd: dma_buf fd (output) + * @offset: location of first pixel, in bytes (output) + * @pitch: length of a scanline including padding, in bytes (output) + * + * Simple buffers are analogous to DRM's "dumb" buffers. They have a single + * plane of linear RGB data which can be allocated and scanned out without + * any driver-private ioctls or data. + * + * @format must be a standard RGB format defined in drm_fourcc.h. + * + * ADF clients must NOT assume that an interface can scan out a simple buffer + * allocated by a different ADF interface, even if the two interfaces belong to + * the same ADF device. + */ +struct adf_simple_buffer_alloc { + __u16 w; + __u16 h; + __u32 format; + + __s64 fd; + __u32 offset; + __u32 pitch; +}; + +/** + * struct adf_simple_post_config - request to flip to a single buffer without + * driver-private data + * + * @buf: description of buffer displayed (input) + * @complete_fence: sync_fence fd which will clear when this buffer has left the + * screen (output) + */ +struct adf_simple_post_config { + struct adf_buffer_config buf; + __s64 complete_fence; +}; + /** * struct adf_attachment_config - description of attachment between an overlay * engine and an interface @@ -196,6 +239,7 @@ struct adf_device_data { * @type: interface type (see enum @adf_interface_type) * @id: which interface of type @type; * e.g. interface DSI.1 -> @type=@ADF_INTF_TYPE_DSI, @id=1 + * @flags: informational flags (bitmask of %ADF_INTF_FLAG_* values) * @dpms_state: DPMS state (one of @DRM_MODE_DPMS_* defined in drm_mode.h) * @hotplug_detect: whether a display is plugged in * @width_mm: screen width in millimeters, or 0 if unknown @@ -249,6 +293,8 @@ struct adf_overlay_engine_data { #define ADF_GET_INTERFACE_DATA _IOR('D', 5, struct adf_interface_data) #define ADF_GET_OVERLAY_ENGINE_DATA \ _IOR('D', 6, struct adf_overlay_engine_data) +#define ADF_SIMPLE_POST_CONFIG _IOW('D', 7, struct adf_simple_post_config) +#define ADF_SIMPLE_BUFFER_ALLOC _IOW('D', 8, struct adf_simple_buffer_alloc) #define ADF_ATTACH _IOW('D', 9, struct adf_attachment_config) #define ADF_DETACH _IOW('D', 10, struct adf_attachment_config) diff --git a/include/video/adf.h b/include/video/adf.h index c09d8dfb4d6c..6c2191a318a5 100644 --- a/include/video/adf.h +++ b/include/video/adf.h @@ -299,6 +299,16 @@ struct adf_device { * @blank: change the display's DPMS state. Return 0 on success or error * code (<0) on failure. * + * @alloc_simple_buffer: allocate a buffer with the specified @w, @h, and + * @format. @format will be a standard RGB format (i.e., + * adf_format_is_rgb(@format) == true). Return 0 on success or error code + * (<0) on failure. On success, return the buffer, offset, and pitch in + * @dma_buf, @offset, and @pitch respectively. + * @describe_simple_post: provide driver-private data needed to post a single + * buffer @buf. Copy up to ADF_MAX_CUSTOM_DATA_SIZE bytes into @data + * (allocated by ADF) and return the number of bytes in @size. Return 0 on + * success or error code (<0) on failure. + * * @modeset: change the interface's mode. @mode is not necessarily part of the * modelist passed to adf_hotplug_notify_connected(); the driver may * accept or reject custom modes at its discretion. Return 0 on success or @@ -317,6 +327,14 @@ struct adf_interface_ops { /* optional */ int (*blank)(struct adf_interface *intf, u8 state); + /* optional */ + int (*alloc_simple_buffer)(struct adf_interface *intf, + u16 w, u16 h, u32 format, + struct dma_buf **dma_buf, u32 *offset, u32 *pitch); + /* optional */ + int (*describe_simple_post)(struct adf_interface *intf, + struct adf_buffer *fb, void *data, size_t *size); + /* optional */ int (*modeset)(struct adf_interface *intf, struct drm_mode_modeinfo *mode); diff --git a/include/video/adf_client.h b/include/video/adf_client.h index 1e471a489983..983f2b6a5890 100644 --- a/include/video/adf_client.h +++ b/include/video/adf_client.h @@ -28,6 +28,10 @@ int adf_interface_set_mode(struct adf_interface *intf, struct drm_mode_modeinfo *mode); int adf_interface_get_screen_size(struct adf_interface *intf, u16 *width, u16 *height); +int adf_interface_simple_buffer_alloc(struct adf_interface *intf, u16 w, u16 h, + u32 format, struct dma_buf **dma_buf, u32 *offset, u32 *pitch); +struct sync_fence *adf_interface_simple_post(struct adf_interface *intf, + struct adf_buffer *buf); bool adf_overlay_engine_supports_format(struct adf_overlay_engine *eng, u32 format);