| /* |
| * Copyright (C) 2013 Google, Inc. |
| * |
| * This software is licensed under the terms of the GNU General Public |
| * License version 2, as published by the Free Software Foundation, and |
| * may be copied, distributed, and modified under those terms. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| */ |
| |
| #include <linux/kthread.h> |
| #include <linux/mutex.h> |
| #include <linux/slab.h> |
| |
| #include "sw_sync.h" |
| |
| #include <video/adf.h> |
| #include <video/adf_client.h> |
| #include <video/adf_format.h> |
| |
| #include "adf.h" |
| |
| static inline bool vsync_active(u8 state) |
| { |
| return state == DRM_MODE_DPMS_ON || state == DRM_MODE_DPMS_STANDBY; |
| } |
| |
| /** |
| * adf_interface_blank - set interface's DPMS state |
| * |
| * @intf: the interface |
| * @state: one of %DRM_MODE_DPMS_* |
| * |
| * Returns 0 on success or -errno on failure. |
| */ |
| int adf_interface_blank(struct adf_interface *intf, u8 state) |
| { |
| struct adf_device *dev = adf_interface_parent(intf); |
| u8 prev_state; |
| bool disable_vsync; |
| bool enable_vsync; |
| int ret = 0; |
| struct adf_event_refcount *vsync_refcount; |
| |
| if (!intf->ops || !intf->ops->blank) |
| return -EOPNOTSUPP; |
| |
| if (state > DRM_MODE_DPMS_OFF) |
| return -EINVAL; |
| |
| mutex_lock(&dev->client_lock); |
| if (state != DRM_MODE_DPMS_ON) |
| flush_kthread_worker(&dev->post_worker); |
| mutex_lock(&intf->base.event_lock); |
| |
| vsync_refcount = adf_obj_find_event_refcount(&intf->base, |
| ADF_EVENT_VSYNC); |
| if (!vsync_refcount) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| prev_state = intf->dpms_state; |
| if (prev_state == state) { |
| ret = -EBUSY; |
| goto done; |
| } |
| |
| disable_vsync = vsync_active(prev_state) && |
| !vsync_active(state) && |
| vsync_refcount->refcount; |
| enable_vsync = !vsync_active(prev_state) && |
| vsync_active(state) && |
| vsync_refcount->refcount; |
| |
| if (disable_vsync) |
| intf->base.ops->set_event(&intf->base, ADF_EVENT_VSYNC, |
| false); |
| |
| ret = intf->ops->blank(intf, state); |
| if (ret < 0) { |
| if (disable_vsync) |
| intf->base.ops->set_event(&intf->base, ADF_EVENT_VSYNC, |
| true); |
| goto done; |
| } |
| |
| if (enable_vsync) |
| intf->base.ops->set_event(&intf->base, ADF_EVENT_VSYNC, |
| true); |
| |
| intf->dpms_state = state; |
| done: |
| mutex_unlock(&intf->base.event_lock); |
| mutex_unlock(&dev->client_lock); |
| return ret; |
| } |
| EXPORT_SYMBOL(adf_interface_blank); |
| |
| /** |
| * adf_interface_blank - get interface's current DPMS state |
| * |
| * @intf: the interface |
| * |
| * Returns one of %DRM_MODE_DPMS_*. |
| */ |
| u8 adf_interface_dpms_state(struct adf_interface *intf) |
| { |
| struct adf_device *dev = adf_interface_parent(intf); |
| u8 dpms_state; |
| |
| mutex_lock(&dev->client_lock); |
| dpms_state = intf->dpms_state; |
| mutex_unlock(&dev->client_lock); |
| |
| return dpms_state; |
| } |
| EXPORT_SYMBOL(adf_interface_dpms_state); |
| |
| /** |
| * adf_interface_current_mode - get interface's current display mode |
| * |
| * @intf: the interface |
| * @mode: returns the current mode |
| */ |
| void adf_interface_current_mode(struct adf_interface *intf, |
| struct drm_mode_modeinfo *mode) |
| { |
| struct adf_device *dev = adf_interface_parent(intf); |
| |
| mutex_lock(&dev->client_lock); |
| memcpy(mode, &intf->current_mode, sizeof(*mode)); |
| mutex_unlock(&dev->client_lock); |
| } |
| EXPORT_SYMBOL(adf_interface_current_mode); |
| |
| /** |
| * adf_interface_modelist - get interface's modelist |
| * |
| * @intf: the interface |
| * @modelist: storage for the modelist (optional) |
| * @n_modes: length of @modelist |
| * |
| * If @modelist is not NULL, adf_interface_modelist() will copy up to @n_modes |
| * modelist entries into @modelist. |
| * |
| * Returns the length of the modelist. |
| */ |
| size_t adf_interface_modelist(struct adf_interface *intf, |
| struct drm_mode_modeinfo *modelist, size_t n_modes) |
| { |
| unsigned long flags; |
| size_t retval; |
| |
| read_lock_irqsave(&intf->hotplug_modelist_lock, flags); |
| if (modelist) |
| memcpy(modelist, intf->modelist, sizeof(modelist[0]) * |
| min(n_modes, intf->n_modes)); |
| retval = intf->n_modes; |
| read_unlock_irqrestore(&intf->hotplug_modelist_lock, flags); |
| |
| return retval; |
| } |
| EXPORT_SYMBOL(adf_interface_modelist); |
| |
| /** |
| * adf_interface_set_mode - set interface's display mode |
| * |
| * @intf: the interface |
| * @mode: the new mode |
| * |
| * Returns 0 on success or -errno on failure. |
| */ |
| int adf_interface_set_mode(struct adf_interface *intf, |
| struct drm_mode_modeinfo *mode) |
| { |
| struct adf_device *dev = adf_interface_parent(intf); |
| int ret = 0; |
| |
| if (!intf->ops || !intf->ops->modeset) |
| return -EOPNOTSUPP; |
| |
| mutex_lock(&dev->client_lock); |
| flush_kthread_worker(&dev->post_worker); |
| |
| ret = intf->ops->modeset(intf, mode); |
| if (ret < 0) |
| goto done; |
| |
| memcpy(&intf->current_mode, mode, sizeof(*mode)); |
| done: |
| mutex_unlock(&dev->client_lock); |
| return ret; |
| } |
| EXPORT_SYMBOL(adf_interface_set_mode); |
| |
| /** |
| * adf_interface_screen_size - get size of screen connected to interface |
| * |
| * @intf: the interface |
| * @width_mm: returns the screen width in mm |
| * @height_mm: returns the screen width in mm |
| * |
| * Returns 0 on success or -errno on failure. |
| */ |
| int adf_interface_get_screen_size(struct adf_interface *intf, u16 *width_mm, |
| u16 *height_mm) |
| { |
| struct adf_device *dev = adf_interface_parent(intf); |
| int ret; |
| |
| if (!intf->ops || !intf->ops->screen_size) |
| return -EOPNOTSUPP; |
| |
| mutex_lock(&dev->client_lock); |
| ret = intf->ops->screen_size(intf, width_mm, height_mm); |
| mutex_unlock(&dev->client_lock); |
| |
| return ret; |
| } |
| EXPORT_SYMBOL(adf_interface_get_screen_size); |
| |
| /** |
| * adf_overlay_engine_supports_format - returns whether a format is in an |
| * overlay engine's supported list |
| * |
| * @eng: the overlay engine |
| * @format: format fourcc |
| */ |
| bool adf_overlay_engine_supports_format(struct adf_overlay_engine *eng, |
| u32 format) |
| { |
| size_t i; |
| for (i = 0; i < eng->ops->n_supported_formats; i++) |
| if (format == eng->ops->supported_formats[i]) |
| return true; |
| |
| return false; |
| } |
| EXPORT_SYMBOL(adf_overlay_engine_supports_format); |
| |
| static int adf_buffer_validate(struct adf_buffer *buf) |
| { |
| struct adf_overlay_engine *eng = buf->overlay_engine; |
| struct device *dev = &eng->base.dev; |
| struct adf_device *parent = adf_overlay_engine_parent(eng); |
| u8 hsub, vsub, num_planes, cpp[ADF_MAX_PLANES], i; |
| |
| if (!adf_overlay_engine_supports_format(eng, buf->format)) { |
| char format_str[ADF_FORMAT_STR_SIZE]; |
| adf_format_str(buf->format, format_str); |
| dev_err(dev, "unsupported format %s\n", format_str); |
| return -EINVAL; |
| } |
| |
| if (!adf_format_is_standard(buf->format)) |
| return parent->ops->validate_custom_format(parent, buf); |
| |
| hsub = adf_format_horz_chroma_subsampling(buf->format); |
| vsub = adf_format_vert_chroma_subsampling(buf->format); |
| num_planes = adf_format_num_planes(buf->format); |
| for (i = 0; i < num_planes; i++) |
| cpp[i] = adf_format_plane_cpp(buf->format, i); |
| |
| return adf_format_validate_yuv(parent, buf, num_planes, hsub, vsub, |
| cpp); |
| } |
| |
| static int adf_buffer_map(struct adf_device *dev, struct adf_buffer *buf, |
| struct adf_buffer_mapping *mapping) |
| { |
| int ret = 0; |
| size_t i; |
| |
| for (i = 0; i < buf->n_planes; i++) { |
| struct dma_buf_attachment *attachment; |
| struct sg_table *sg_table; |
| |
| attachment = dma_buf_attach(buf->dma_bufs[i], dev->dev); |
| if (IS_ERR(attachment)) { |
| ret = PTR_ERR(attachment); |
| dev_err(&dev->base.dev, "attaching plane %u failed: %d\n", |
| i, ret); |
| goto done; |
| } |
| mapping->attachments[i] = attachment; |
| |
| sg_table = dma_buf_map_attachment(attachment, DMA_TO_DEVICE); |
| if (IS_ERR(sg_table)) { |
| ret = PTR_ERR(sg_table); |
| dev_err(&dev->base.dev, "mapping plane %u failed: %d", |
| i, ret); |
| goto done; |
| } else if (!sg_table) { |
| ret = -ENOMEM; |
| dev_err(&dev->base.dev, "mapping plane %u failed\n", i); |
| goto done; |
| } |
| mapping->sg_tables[i] = sg_table; |
| } |
| |
| done: |
| if (ret < 0) |
| adf_buffer_mapping_cleanup(mapping, buf); |
| |
| return ret; |
| } |
| |
| static struct sync_fence *adf_sw_complete_fence(struct adf_device *dev) |
| { |
| struct sync_pt *pt; |
| struct sync_fence *complete_fence; |
| |
| if (!dev->timeline) { |
| dev->timeline = sw_sync_timeline_create(dev->base.name); |
| if (!dev->timeline) |
| return ERR_PTR(-ENOMEM); |
| dev->timeline_max = 1; |
| } |
| |
| dev->timeline_max++; |
| pt = sw_sync_pt_create(dev->timeline, dev->timeline_max); |
| if (!pt) |
| goto err_pt_create; |
| complete_fence = sync_fence_create(dev->base.name, pt); |
| if (!complete_fence) |
| goto err_fence_create; |
| |
| return complete_fence; |
| |
| err_fence_create: |
| sync_pt_free(pt); |
| err_pt_create: |
| dev->timeline_max--; |
| return ERR_PTR(-ENOSYS); |
| } |
| |
| /** |
| * adf_device_post - flip to a new set of buffers |
| * |
| * @dev: device targeted by the flip |
| * @intfs: interfaces targeted by the flip |
| * @n_intfs: number of targeted interfaces |
| * @bufs: description of buffers displayed |
| * @n_bufs: number of buffers displayed |
| * @custom_data: driver-private data |
| * @custom_data_size: size of driver-private data |
| * |
| * adf_device_post() will copy @intfs, @bufs, and @custom_data, so they may |
| * point to variables on the stack. adf_device_post() also takes its own |
| * reference on each of the dma-bufs in @bufs. The adf_device_post_nocopy() |
| * variant transfers ownership of these resources to ADF instead. |
| * |
| * On success, returns a sync fence which signals when the buffers are removed |
| * from the screen. On failure, returns ERR_PTR(-errno). |
| */ |
| struct sync_fence *adf_device_post(struct adf_device *dev, |
| struct adf_interface **intfs, size_t n_intfs, |
| struct adf_buffer *bufs, size_t n_bufs, void *custom_data, |
| size_t custom_data_size) |
| { |
| struct adf_interface **intfs_copy = NULL; |
| struct adf_buffer *bufs_copy = NULL; |
| void *custom_data_copy = NULL; |
| struct sync_fence *ret; |
| size_t i; |
| |
| intfs_copy = kzalloc(sizeof(intfs_copy[0]) * n_intfs, GFP_KERNEL); |
| if (!intfs_copy) |
| return ERR_PTR(-ENOMEM); |
| |
| bufs_copy = kzalloc(sizeof(bufs_copy[0]) * n_bufs, GFP_KERNEL); |
| if (!bufs_copy) { |
| ret = ERR_PTR(-ENOMEM); |
| goto err_alloc; |
| } |
| |
| custom_data_copy = kzalloc(custom_data_size, GFP_KERNEL); |
| if (!custom_data_copy) { |
| ret = ERR_PTR(-ENOMEM); |
| goto err_alloc; |
| } |
| |
| for (i = 0; i < n_bufs; i++) { |
| size_t j; |
| for (j = 0; j < bufs[i].n_planes; j++) |
| get_dma_buf(bufs[i].dma_bufs[j]); |
| } |
| |
| memcpy(intfs_copy, intfs, sizeof(intfs_copy[0]) * n_intfs); |
| memcpy(bufs_copy, bufs, sizeof(bufs_copy[0]) * n_bufs); |
| memcpy(custom_data_copy, custom_data, custom_data_size); |
| |
| ret = adf_device_post_nocopy(dev, intfs_copy, n_intfs, bufs_copy, |
| n_bufs, custom_data_copy, custom_data_size); |
| if (IS_ERR(ret)) |
| goto err_post; |
| |
| return ret; |
| |
| err_post: |
| for (i = 0; i < n_bufs; i++) { |
| size_t j; |
| for (j = 0; j < bufs[i].n_planes; j++) |
| dma_buf_put(bufs[i].dma_bufs[j]); |
| } |
| err_alloc: |
| kfree(custom_data_copy); |
| kfree(bufs_copy); |
| kfree(intfs_copy); |
| return ret; |
| } |
| EXPORT_SYMBOL(adf_device_post); |
| |
| /** |
| * adf_device_post_nocopy - flip to a new set of buffers |
| * |
| * adf_device_post_nocopy() has the same behavior as adf_device_post(), |
| * except ADF does not copy @intfs, @bufs, or @custom_data, and it does |
| * not take an extra reference on the dma-bufs in @bufs. |
| * |
| * @intfs, @bufs, and @custom_data must point to buffers allocated by |
| * kmalloc(). On success, ADF takes ownership of these buffers and the dma-bufs |
| * in @bufs, and will kfree()/dma_buf_put() them when they are no longer needed. |
| * On failure, adf_device_post_nocopy() does NOT take ownership of these |
| * buffers or the dma-bufs, and the caller must clean them up. |
| * |
| * adf_device_post_nocopy() is mainly intended for implementing ADF's ioctls. |
| * Clients may find the nocopy variant useful in limited cases, but most should |
| * call adf_device_post() instead. |
| */ |
| struct sync_fence *adf_device_post_nocopy(struct adf_device *dev, |
| struct adf_interface **intfs, size_t n_intfs, |
| struct adf_buffer *bufs, size_t n_bufs, |
| void *custom_data, size_t custom_data_size) |
| { |
| struct adf_pending_post *cfg; |
| struct adf_buffer_mapping *mappings; |
| struct sync_fence *ret; |
| size_t i; |
| int err; |
| |
| cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); |
| if (!cfg) |
| return ERR_PTR(-ENOMEM); |
| |
| mappings = kzalloc(sizeof(mappings[0]) * n_bufs, GFP_KERNEL); |
| if (!mappings) { |
| ret = ERR_PTR(-ENOMEM); |
| goto err_alloc; |
| } |
| |
| mutex_lock(&dev->client_lock); |
| |
| for (i = 0; i < n_bufs; i++) { |
| err = adf_buffer_validate(&bufs[i]); |
| if (err < 0) { |
| ret = ERR_PTR(err); |
| goto err_buf; |
| } |
| |
| err = adf_buffer_map(dev, &bufs[i], &mappings[i]); |
| if (err < 0) { |
| ret = ERR_PTR(err); |
| goto err_buf; |
| } |
| } |
| |
| INIT_LIST_HEAD(&cfg->head); |
| cfg->config.n_bufs = n_bufs; |
| cfg->config.bufs = bufs; |
| cfg->config.mappings = mappings; |
| cfg->config.custom_data = custom_data; |
| cfg->config.custom_data_size = custom_data_size; |
| |
| err = dev->ops->validate(dev, &cfg->config, &cfg->state); |
| if (err < 0) { |
| ret = ERR_PTR(err); |
| goto err_buf; |
| } |
| |
| mutex_lock(&dev->post_lock); |
| |
| if (dev->ops->complete_fence) |
| ret = dev->ops->complete_fence(dev, &cfg->config, |
| cfg->state); |
| else |
| ret = adf_sw_complete_fence(dev); |
| |
| if (IS_ERR(ret)) |
| goto err_fence; |
| |
| list_add_tail(&cfg->head, &dev->post_list); |
| queue_kthread_work(&dev->post_worker, &dev->post_work); |
| mutex_unlock(&dev->post_lock); |
| mutex_unlock(&dev->client_lock); |
| kfree(intfs); |
| return ret; |
| |
| err_fence: |
| mutex_unlock(&dev->post_lock); |
| |
| err_buf: |
| for (i = 0; i < n_bufs; i++) |
| adf_buffer_mapping_cleanup(&mappings[i], &bufs[i]); |
| |
| mutex_unlock(&dev->client_lock); |
| kfree(mappings); |
| |
| err_alloc: |
| kfree(cfg); |
| return ret; |
| } |
| EXPORT_SYMBOL(adf_device_post_nocopy); |
| |
| static void adf_attachment_list_to_array(struct adf_device *dev, |
| struct list_head *src, struct adf_attachment *dst, size_t size) |
| { |
| struct adf_attachment_list *entry; |
| size_t i = 0; |
| |
| if (!dst) |
| return; |
| |
| list_for_each_entry(entry, src, head) { |
| if (i == size) |
| return; |
| dst[i] = entry->attachment; |
| i++; |
| } |
| } |
| |
| /** |
| * adf_device_attachments - get device's list of active attachments |
| * |
| * @dev: the device |
| * @attachments: storage for the attachment list (optional) |
| * @n_attachments: length of @attachments |
| * |
| * If @attachments is not NULL, adf_device_attachments() will copy up to |
| * @n_attachments entries into @attachments. |
| * |
| * Returns the length of the active attachment list. |
| */ |
| size_t adf_device_attachments(struct adf_device *dev, |
| struct adf_attachment *attachments, size_t n_attachments) |
| { |
| size_t retval; |
| |
| mutex_lock(&dev->client_lock); |
| adf_attachment_list_to_array(dev, &dev->attached, attachments, |
| n_attachments); |
| retval = dev->n_attached; |
| mutex_unlock(&dev->client_lock); |
| |
| return retval; |
| } |
| EXPORT_SYMBOL(adf_device_attachments); |
| |
| /** |
| * adf_device_attachments_allowed - get device's list of allowed attachments |
| * |
| * @dev: the device |
| * @attachments: storage for the attachment list (optional) |
| * @n_attachments: length of @attachments |
| * |
| * If @attachments is not NULL, adf_device_attachments_allowed() will copy up to |
| * @n_attachments entries into @attachments. |
| * |
| * Returns the length of the allowed attachment list. |
| */ |
| size_t adf_device_attachments_allowed(struct adf_device *dev, |
| struct adf_attachment *attachments, size_t n_attachments) |
| { |
| size_t retval; |
| |
| mutex_lock(&dev->client_lock); |
| adf_attachment_list_to_array(dev, &dev->attach_allowed, attachments, |
| n_attachments); |
| retval = dev->n_attach_allowed; |
| mutex_unlock(&dev->client_lock); |
| |
| return retval; |
| } |
| EXPORT_SYMBOL(adf_device_attachments_allowed); |
| |
| /** |
| * adf_device_attached - return whether an overlay engine and interface are |
| * attached |
| * |
| * @dev: the parent device |
| * @eng: the overlay engine |
| * @intf: the interface |
| */ |
| bool adf_device_attached(struct adf_device *dev, struct adf_overlay_engine *eng, |
| struct adf_interface *intf) |
| { |
| struct adf_attachment_list *attachment; |
| |
| mutex_lock(&dev->client_lock); |
| attachment = adf_attachment_find(&dev->attached, eng, intf); |
| mutex_unlock(&dev->client_lock); |
| |
| return attachment != NULL; |
| } |
| EXPORT_SYMBOL(adf_device_attached); |
| |
| /** |
| * adf_device_attach_allowed - return whether the ADF device supports attaching |
| * an overlay engine and interface |
| * |
| * @dev: the parent device |
| * @eng: the overlay engine |
| * @intf: the interface |
| */ |
| bool adf_device_attach_allowed(struct adf_device *dev, |
| struct adf_overlay_engine *eng, struct adf_interface *intf) |
| { |
| struct adf_attachment_list *attachment; |
| |
| mutex_lock(&dev->client_lock); |
| attachment = adf_attachment_find(&dev->attach_allowed, eng, intf); |
| mutex_unlock(&dev->client_lock); |
| |
| return attachment != NULL; |
| } |
| EXPORT_SYMBOL(adf_device_attach_allowed); |
| /** |
| * adf_device_attach - attach an overlay engine to an interface |
| * |
| * @dev: the parent device |
| * @eng: the overlay engine |
| * @intf: the interface |
| * |
| * Returns 0 on success, -%EINVAL if attaching @intf and @eng is not allowed, |
| * -%EALREADY if @intf and @eng are already attached, or -errno on any other |
| * failure. |
| */ |
| int adf_device_attach(struct adf_device *dev, struct adf_overlay_engine *eng, |
| struct adf_interface *intf) |
| { |
| int ret; |
| struct adf_attachment_list *attachment = NULL; |
| |
| ret = adf_attachment_validate(dev, eng, intf); |
| if (ret < 0) |
| return ret; |
| |
| mutex_lock(&dev->client_lock); |
| |
| if (dev->n_attached == ADF_MAX_ATTACHMENTS) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| if (!adf_attachment_find(&dev->attach_allowed, eng, intf)) { |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| if (adf_attachment_find(&dev->attached, eng, intf)) { |
| ret = -EALREADY; |
| goto done; |
| } |
| |
| ret = adf_device_attach_op(dev, eng, intf); |
| if (ret < 0) |
| goto done; |
| |
| attachment = kzalloc(sizeof(*attachment), GFP_KERNEL); |
| if (!attachment) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| attachment->attachment.interface = intf; |
| attachment->attachment.overlay_engine = eng; |
| list_add_tail(&attachment->head, &dev->attached); |
| dev->n_attached++; |
| |
| done: |
| mutex_unlock(&dev->client_lock); |
| if (ret < 0) |
| kfree(attachment); |
| |
| return ret; |
| } |
| EXPORT_SYMBOL(adf_device_attach); |
| |
| /** |
| * adf_device_detach - detach an overlay engine from an interface |
| * |
| * @dev: the parent device |
| * @eng: the overlay engine |
| * @intf: the interface |
| * |
| * Returns 0 on success, -%EINVAL if @intf and @eng are not attached, |
| * or -errno on any other failure. |
| */ |
| int adf_device_detach(struct adf_device *dev, struct adf_overlay_engine *eng, |
| struct adf_interface *intf) |
| { |
| int ret; |
| struct adf_attachment_list *attachment; |
| |
| ret = adf_attachment_validate(dev, eng, intf); |
| if (ret < 0) |
| return ret; |
| |
| mutex_lock(&dev->client_lock); |
| |
| attachment = adf_attachment_find(&dev->attached, eng, intf); |
| if (!attachment) { |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| ret = adf_device_detach_op(dev, eng, intf); |
| if (ret < 0) |
| goto done; |
| |
| adf_attachment_free(attachment); |
| dev->n_attached--; |
| done: |
| mutex_unlock(&dev->client_lock); |
| 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); |