123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188 |
- /*
- * Copyright (C) 2013 Google, Inc.
- * adf_modeinfo_{set_name,set_vrefresh} modified from
- * drivers/gpu/drm/drm_modes.c
- * adf_format_validate_yuv modified from framebuffer_check in
- * drivers/gpu/drm/drm_crtc.c
- *
- * 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/device.h>
- #include <linux/idr.h>
- #include <linux/highmem.h>
- #include <linux/memblock.h>
- #include <linux/module.h>
- #include <linux/platform_device.h>
- #include <linux/slab.h>
- #include <video/adf_format.h>
- #include "sw_sync.h"
- #include "sync.h"
- #include "adf.h"
- #include "adf_fops.h"
- #include "adf_sysfs.h"
- #define CREATE_TRACE_POINTS
- #include "adf_trace.h"
- #define ADF_SHORT_FENCE_TIMEOUT (1 * MSEC_PER_SEC)
- #define ADF_LONG_FENCE_TIMEOUT (10 * MSEC_PER_SEC)
- static DEFINE_IDR(adf_devices);
- static void adf_fence_wait(struct adf_device *dev, struct sync_fence *fence)
- {
- /* sync_fence_wait() dumps debug information on timeout. Experience
- has shown that if the pipeline gets stuck, a short timeout followed
- by a longer one provides useful information for debugging. */
- int err = sync_fence_wait(fence, ADF_SHORT_FENCE_TIMEOUT);
- if (err >= 0)
- return;
- if (err == -ETIME)
- err = sync_fence_wait(fence, ADF_LONG_FENCE_TIMEOUT);
- if (err < 0)
- dev_warn(&dev->base.dev, "error waiting on fence: %d\n", err);
- }
- void adf_buffer_cleanup(struct adf_buffer *buf)
- {
- size_t i;
- for (i = 0; i < ARRAY_SIZE(buf->dma_bufs); i++)
- if (buf->dma_bufs[i])
- dma_buf_put(buf->dma_bufs[i]);
- if (buf->acquire_fence)
- sync_fence_put(buf->acquire_fence);
- }
- void adf_buffer_mapping_cleanup(struct adf_buffer_mapping *mapping,
- struct adf_buffer *buf)
- {
- /* calling adf_buffer_mapping_cleanup() is safe even if mapping is
- uninitialized or partially-initialized, as long as it was
- zeroed on allocation */
- size_t i;
- for (i = 0; i < ARRAY_SIZE(mapping->sg_tables); i++) {
- if (mapping->sg_tables[i])
- dma_buf_unmap_attachment(mapping->attachments[i],
- mapping->sg_tables[i], DMA_TO_DEVICE);
- if (mapping->attachments[i])
- dma_buf_detach(buf->dma_bufs[i],
- mapping->attachments[i]);
- }
- }
- void adf_post_cleanup(struct adf_device *dev, struct adf_pending_post *post)
- {
- size_t i;
- if (post->state)
- dev->ops->state_free(dev, post->state);
- for (i = 0; i < post->config.n_bufs; i++) {
- adf_buffer_mapping_cleanup(&post->config.mappings[i],
- &post->config.bufs[i]);
- adf_buffer_cleanup(&post->config.bufs[i]);
- }
- kfree(post->config.custom_data);
- kfree(post->config.mappings);
- kfree(post->config.bufs);
- kfree(post);
- }
- static void adf_sw_advance_timeline(struct adf_device *dev)
- {
- #ifdef CONFIG_SW_SYNC
- sw_sync_timeline_inc(dev->timeline, 1);
- #else
- BUG();
- #endif
- }
- static void adf_post_work_func(struct kthread_work *work)
- {
- struct adf_device *dev =
- container_of(work, struct adf_device, post_work);
- struct adf_pending_post *post, *next;
- struct list_head saved_list;
- mutex_lock(&dev->post_lock);
- memcpy(&saved_list, &dev->post_list, sizeof(saved_list));
- list_replace_init(&dev->post_list, &saved_list);
- mutex_unlock(&dev->post_lock);
- list_for_each_entry_safe(post, next, &saved_list, head) {
- int i;
- for (i = 0; i < post->config.n_bufs; i++) {
- struct sync_fence *fence =
- post->config.bufs[i].acquire_fence;
- if (fence)
- adf_fence_wait(dev, fence);
- }
- dev->ops->post(dev, &post->config, post->state);
- if (dev->ops->advance_timeline)
- dev->ops->advance_timeline(dev, &post->config,
- post->state);
- else
- adf_sw_advance_timeline(dev);
- list_del(&post->head);
- if (dev->onscreen)
- adf_post_cleanup(dev, dev->onscreen);
- dev->onscreen = post;
- }
- }
- void adf_attachment_free(struct adf_attachment_list *attachment)
- {
- list_del(&attachment->head);
- kfree(attachment);
- }
- struct adf_event_refcount *adf_obj_find_event_refcount(struct adf_obj *obj,
- enum adf_event_type type)
- {
- struct rb_root *root = &obj->event_refcount;
- struct rb_node **new = &(root->rb_node);
- struct rb_node *parent = NULL;
- struct adf_event_refcount *refcount;
- while (*new) {
- refcount = container_of(*new, struct adf_event_refcount, node);
- parent = *new;
- if (refcount->type > type)
- new = &(*new)->rb_left;
- else if (refcount->type < type)
- new = &(*new)->rb_right;
- else
- return refcount;
- }
- refcount = kzalloc(sizeof(*refcount), GFP_KERNEL);
- if (!refcount)
- return NULL;
- refcount->type = type;
- rb_link_node(&refcount->node, parent, new);
- rb_insert_color(&refcount->node, root);
- return refcount;
- }
- /**
- * adf_event_get - increase the refcount for an event
- *
- * @obj: the object that produces the event
- * @type: the event type
- *
- * ADF will call the object's set_event() op if needed. ops are allowed
- * to sleep, so adf_event_get() must NOT be called from an atomic context.
- *
- * Returns 0 if successful, or -%EINVAL if the object does not support the
- * requested event type.
- */
- int adf_event_get(struct adf_obj *obj, enum adf_event_type type)
- {
- struct adf_event_refcount *refcount;
- int old_refcount;
- int ret;
- ret = adf_obj_check_supports_event(obj, type);
- if (ret < 0)
- return ret;
- mutex_lock(&obj->event_lock);
- refcount = adf_obj_find_event_refcount(obj, type);
- if (!refcount) {
- ret = -ENOMEM;
- goto done;
- }
- old_refcount = refcount->refcount++;
- if (old_refcount == 0) {
- obj->ops->set_event(obj, type, true);
- trace_adf_event_enable(obj, type);
- }
- done:
- mutex_unlock(&obj->event_lock);
- return ret;
- }
- EXPORT_SYMBOL(adf_event_get);
- /**
- * adf_event_put - decrease the refcount for an event
- *
- * @obj: the object that produces the event
- * @type: the event type
- *
- * ADF will call the object's set_event() op if needed. ops are allowed
- * to sleep, so adf_event_put() must NOT be called from an atomic context.
- *
- * Returns 0 if successful, -%EINVAL if the object does not support the
- * requested event type, or -%EALREADY if the refcount is already 0.
- */
- int adf_event_put(struct adf_obj *obj, enum adf_event_type type)
- {
- struct adf_event_refcount *refcount;
- int old_refcount;
- int ret;
- ret = adf_obj_check_supports_event(obj, type);
- if (ret < 0)
- return ret;
- mutex_lock(&obj->event_lock);
- refcount = adf_obj_find_event_refcount(obj, type);
- if (!refcount) {
- ret = -ENOMEM;
- goto done;
- }
- old_refcount = refcount->refcount--;
- if (WARN_ON(old_refcount == 0)) {
- refcount->refcount++;
- ret = -EALREADY;
- } else if (old_refcount == 1) {
- obj->ops->set_event(obj, type, false);
- trace_adf_event_disable(obj, type);
- }
- done:
- mutex_unlock(&obj->event_lock);
- return ret;
- }
- EXPORT_SYMBOL(adf_event_put);
- /**
- * adf_vsync_wait - wait for a vsync event on a display interface
- *
- * @intf: the display interface
- * @timeout: timeout in jiffies (0 = wait indefinitely)
- *
- * adf_vsync_wait() may sleep, so it must NOT be called from an atomic context.
- *
- * This function returns -%ERESTARTSYS if it is interrupted by a signal.
- * If @timeout == 0 then this function returns 0 on vsync. If @timeout > 0 then
- * this function returns the number of remaining jiffies or -%ETIMEDOUT on
- * timeout.
- */
- int adf_vsync_wait(struct adf_interface *intf, long timeout)
- {
- ktime_t timestamp;
- int ret;
- unsigned long flags;
- read_lock_irqsave(&intf->vsync_lock, flags);
- timestamp = intf->vsync_timestamp;
- read_unlock_irqrestore(&intf->vsync_lock, flags);
- adf_vsync_get(intf);
- if (timeout) {
- ret = wait_event_interruptible_timeout(intf->vsync_wait,
- !ktime_equal(timestamp,
- intf->vsync_timestamp),
- msecs_to_jiffies(timeout));
- if (ret == 0 && ktime_equal(timestamp, intf->vsync_timestamp))
- ret = -ETIMEDOUT;
- } else {
- ret = wait_event_interruptible(intf->vsync_wait,
- !ktime_equal(timestamp,
- intf->vsync_timestamp));
- }
- adf_vsync_put(intf);
- return ret;
- }
- EXPORT_SYMBOL(adf_vsync_wait);
- static void adf_event_queue(struct adf_obj *obj, struct adf_event *event)
- {
- struct adf_file *file;
- unsigned long flags;
- trace_adf_event(obj, event->type);
- spin_lock_irqsave(&obj->file_lock, flags);
- list_for_each_entry(file, &obj->file_list, head)
- if (test_bit(event->type, file->event_subscriptions))
- adf_file_queue_event(file, event);
- spin_unlock_irqrestore(&obj->file_lock, flags);
- }
- /**
- * adf_event_notify - notify userspace of a driver-private event
- *
- * @obj: the ADF object that produced the event
- * @event: the event
- *
- * adf_event_notify() may be called safely from an atomic context. It will
- * copy @event if needed, so @event may point to a variable on the stack.
- *
- * Drivers must NOT call adf_event_notify() for vsync and hotplug events.
- * ADF provides adf_vsync_notify() and
- * adf_hotplug_notify_{connected,disconnected}() for these events.
- */
- int adf_event_notify(struct adf_obj *obj, struct adf_event *event)
- {
- if (WARN_ON(event->type == ADF_EVENT_VSYNC ||
- event->type == ADF_EVENT_HOTPLUG))
- return -EINVAL;
- adf_event_queue(obj, event);
- return 0;
- }
- EXPORT_SYMBOL(adf_event_notify);
- /**
- * adf_vsync_notify - notify ADF of a display interface's vsync event
- *
- * @intf: the display interface
- * @timestamp: the time the vsync occurred
- *
- * adf_vsync_notify() may be called safely from an atomic context.
- */
- void adf_vsync_notify(struct adf_interface *intf, ktime_t timestamp)
- {
- unsigned long flags;
- struct adf_vsync_event event;
- write_lock_irqsave(&intf->vsync_lock, flags);
- intf->vsync_timestamp = timestamp;
- write_unlock_irqrestore(&intf->vsync_lock, flags);
- wake_up_interruptible_all(&intf->vsync_wait);
- event.base.type = ADF_EVENT_VSYNC;
- event.base.length = sizeof(event);
- event.timestamp = ktime_to_ns(timestamp);
- adf_event_queue(&intf->base, &event.base);
- }
- EXPORT_SYMBOL(adf_vsync_notify);
- void adf_hotplug_notify(struct adf_interface *intf, bool connected,
- struct drm_mode_modeinfo *modelist, size_t n_modes)
- {
- unsigned long flags;
- struct adf_hotplug_event event;
- struct drm_mode_modeinfo *old_modelist;
- write_lock_irqsave(&intf->hotplug_modelist_lock, flags);
- old_modelist = intf->modelist;
- intf->hotplug_detect = connected;
- intf->modelist = modelist;
- intf->n_modes = n_modes;
- write_unlock_irqrestore(&intf->hotplug_modelist_lock, flags);
- kfree(old_modelist);
- event.base.length = sizeof(event);
- event.base.type = ADF_EVENT_HOTPLUG;
- event.connected = connected;
- adf_event_queue(&intf->base, &event.base);
- }
- /**
- * adf_hotplug_notify_connected - notify ADF of a display interface being
- * connected to a display
- *
- * @intf: the display interface
- * @modelist: hardware modes supported by display
- * @n_modes: length of modelist
- *
- * @modelist is copied as needed, so it may point to a variable on the stack.
- *
- * adf_hotplug_notify_connected() may NOT be called safely from an atomic
- * context.
- *
- * Returns 0 on success or error code (<0) on error.
- */
- int adf_hotplug_notify_connected(struct adf_interface *intf,
- struct drm_mode_modeinfo *modelist, size_t n_modes)
- {
- struct drm_mode_modeinfo *modelist_copy;
- if (n_modes > ADF_MAX_MODES)
- return -ENOMEM;
- modelist_copy = kzalloc(sizeof(modelist_copy[0]) * n_modes,
- GFP_KERNEL);
- if (!modelist_copy)
- return -ENOMEM;
- memcpy(modelist_copy, modelist, sizeof(modelist_copy[0]) * n_modes);
- adf_hotplug_notify(intf, true, modelist_copy, n_modes);
- return 0;
- }
- EXPORT_SYMBOL(adf_hotplug_notify_connected);
- /**
- * adf_hotplug_notify_disconnected - notify ADF of a display interface being
- * disconnected from a display
- *
- * @intf: the display interface
- *
- * adf_hotplug_notify_disconnected() may be called safely from an atomic
- * context.
- */
- void adf_hotplug_notify_disconnected(struct adf_interface *intf)
- {
- adf_hotplug_notify(intf, false, NULL, 0);
- }
- EXPORT_SYMBOL(adf_hotplug_notify_disconnected);
- static int adf_obj_init(struct adf_obj *obj, enum adf_obj_type type,
- struct idr *idr, struct adf_device *parent,
- const struct adf_obj_ops *ops, const char *fmt, va_list args)
- {
- int ret;
- if (ops && ops->supports_event && !ops->set_event) {
- pr_err("%s: %s implements supports_event but not set_event\n",
- __func__, adf_obj_type_str(type));
- return -EINVAL;
- }
- ret = idr_alloc(idr, obj, 0, 0, GFP_KERNEL);
- if (ret < 0) {
- pr_err("%s: allocating object id failed: %d\n", __func__, ret);
- return ret;
- }
- obj->id = ret;
- vscnprintf(obj->name, sizeof(obj->name), fmt, args);
- obj->type = type;
- obj->ops = ops;
- obj->parent = parent;
- mutex_init(&obj->event_lock);
- obj->event_refcount = RB_ROOT;
- spin_lock_init(&obj->file_lock);
- INIT_LIST_HEAD(&obj->file_list);
- return 0;
- }
- static void adf_obj_destroy(struct adf_obj *obj, struct idr *idr)
- {
- struct rb_node *node = rb_first(&obj->event_refcount);
- while (node) {
- struct adf_event_refcount *refcount =
- container_of(node, struct adf_event_refcount,
- node);
- rb_erase(&refcount->node, &obj->event_refcount);
- kfree(refcount);
- node = rb_first(&obj->event_refcount);
- }
- mutex_destroy(&obj->event_lock);
- idr_remove(idr, obj->id);
- }
- /**
- * adf_device_init - initialize ADF-internal data for a display device
- * and create sysfs entries
- *
- * @dev: the display device
- * @parent: the device's parent device
- * @ops: the device's associated ops
- * @fmt: formatting string for the display device's name
- *
- * @fmt specifies the device's sysfs filename and the name returned to
- * userspace through the %ADF_GET_DEVICE_DATA ioctl.
- *
- * Returns 0 on success or error code (<0) on failure.
- */
- int adf_device_init(struct adf_device *dev, struct device *parent,
- const struct adf_device_ops *ops, const char *fmt, ...)
- {
- int ret;
- va_list args;
- if (!ops->validate || !ops->post) {
- pr_err("%s: device must implement validate and post\n",
- __func__);
- return -EINVAL;
- }
- if (!ops->complete_fence && !ops->advance_timeline) {
- if (!IS_ENABLED(CONFIG_SW_SYNC)) {
- pr_err("%s: device requires sw_sync but it is not enabled in the kernel\n",
- __func__);
- return -EINVAL;
- }
- } else if (!(ops->complete_fence && ops->advance_timeline)) {
- pr_err("%s: device must implement both complete_fence and advance_timeline, or implement neither\n",
- __func__);
- return -EINVAL;
- }
- memset(dev, 0, sizeof(*dev));
- va_start(args, fmt);
- ret = adf_obj_init(&dev->base, ADF_OBJ_DEVICE, &adf_devices, dev,
- &ops->base, fmt, args);
- va_end(args);
- if (ret < 0)
- return ret;
- dev->dev = parent;
- dev->ops = ops;
- idr_init(&dev->overlay_engines);
- idr_init(&dev->interfaces);
- mutex_init(&dev->client_lock);
- INIT_LIST_HEAD(&dev->post_list);
- mutex_init(&dev->post_lock);
- init_kthread_worker(&dev->post_worker);
- INIT_LIST_HEAD(&dev->attached);
- INIT_LIST_HEAD(&dev->attach_allowed);
- dev->post_thread = kthread_run(kthread_worker_fn,
- &dev->post_worker, dev->base.name);
- if (IS_ERR(dev->post_thread)) {
- ret = PTR_ERR(dev->post_thread);
- dev->post_thread = NULL;
- pr_err("%s: failed to run config posting thread: %d\n",
- __func__, ret);
- goto err;
- }
- init_kthread_work(&dev->post_work, adf_post_work_func);
- ret = adf_device_sysfs_init(dev);
- if (ret < 0)
- goto err;
- return 0;
- err:
- adf_device_destroy(dev);
- return ret;
- }
- EXPORT_SYMBOL(adf_device_init);
- /**
- * adf_device_destroy - clean up ADF-internal data for a display device
- *
- * @dev: the display device
- */
- void adf_device_destroy(struct adf_device *dev)
- {
- struct adf_attachment_list *entry, *next;
- idr_destroy(&dev->interfaces);
- idr_destroy(&dev->overlay_engines);
- if (dev->post_thread) {
- flush_kthread_worker(&dev->post_worker);
- kthread_stop(dev->post_thread);
- }
- if (dev->onscreen)
- adf_post_cleanup(dev, dev->onscreen);
- adf_device_sysfs_destroy(dev);
- list_for_each_entry_safe(entry, next, &dev->attach_allowed, head) {
- adf_attachment_free(entry);
- }
- list_for_each_entry_safe(entry, next, &dev->attached, head) {
- adf_attachment_free(entry);
- }
- mutex_destroy(&dev->post_lock);
- mutex_destroy(&dev->client_lock);
- if (dev->timeline)
- sync_timeline_destroy(&dev->timeline->obj);
- adf_obj_destroy(&dev->base, &adf_devices);
- }
- EXPORT_SYMBOL(adf_device_destroy);
- /**
- * adf_interface_init - initialize ADF-internal data for a display interface
- * and create sysfs entries
- *
- * @intf: the display interface
- * @dev: the interface's "parent" display device
- * @type: interface type (see enum @adf_interface_type)
- * @idx: which interface of type @type;
- * e.g. interface DSI.1 -> @type=%ADF_INTF_TYPE_DSI, @idx=1
- * @flags: informational flags (bitmask of %ADF_INTF_FLAG_* values)
- * @ops: the interface's associated ops
- * @fmt: formatting string for the display interface's name
- *
- * @dev must have previously been initialized with adf_device_init().
- *
- * @fmt affects the name returned to userspace through the
- * %ADF_GET_INTERFACE_DATA ioctl. It does not affect the sysfs filename,
- * which is derived from @dev's name.
- *
- * Returns 0 on success or error code (<0) on failure.
- */
- int adf_interface_init(struct adf_interface *intf, struct adf_device *dev,
- enum adf_interface_type type, u32 idx, u32 flags,
- const struct adf_interface_ops *ops, const char *fmt, ...)
- {
- int ret;
- va_list args;
- const u32 allowed_flags = ADF_INTF_FLAG_PRIMARY |
- ADF_INTF_FLAG_EXTERNAL;
- if (dev->n_interfaces == ADF_MAX_INTERFACES) {
- pr_err("%s: parent device %s has too many interfaces\n",
- __func__, dev->base.name);
- return -ENOMEM;
- }
- if (type >= ADF_INTF_MEMORY && type <= ADF_INTF_TYPE_DEVICE_CUSTOM) {
- pr_err("%s: invalid interface type %u\n", __func__, type);
- return -EINVAL;
- }
- if (flags & ~allowed_flags) {
- pr_err("%s: invalid interface flags 0x%X\n", __func__,
- flags & ~allowed_flags);
- return -EINVAL;
- }
- memset(intf, 0, sizeof(*intf));
- va_start(args, fmt);
- ret = adf_obj_init(&intf->base, ADF_OBJ_INTERFACE, &dev->interfaces,
- dev, ops ? &ops->base : NULL, fmt, args);
- va_end(args);
- if (ret < 0)
- return ret;
- intf->type = type;
- intf->idx = idx;
- intf->flags = flags;
- intf->ops = ops;
- intf->dpms_state = DRM_MODE_DPMS_OFF;
- init_waitqueue_head(&intf->vsync_wait);
- rwlock_init(&intf->vsync_lock);
- rwlock_init(&intf->hotplug_modelist_lock);
- ret = adf_interface_sysfs_init(intf);
- if (ret < 0)
- goto err;
- dev->n_interfaces++;
- return 0;
- err:
- adf_obj_destroy(&intf->base, &dev->interfaces);
- return ret;
- }
- EXPORT_SYMBOL(adf_interface_init);
- /**
- * adf_interface_destroy - clean up ADF-internal data for a display interface
- *
- * @intf: the display interface
- */
- void adf_interface_destroy(struct adf_interface *intf)
- {
- struct adf_device *dev = adf_interface_parent(intf);
- struct adf_attachment_list *entry, *next;
- mutex_lock(&dev->client_lock);
- list_for_each_entry_safe(entry, next, &dev->attach_allowed, head) {
- if (entry->attachment.interface == intf) {
- adf_attachment_free(entry);
- dev->n_attach_allowed--;
- }
- }
- list_for_each_entry_safe(entry, next, &dev->attached, head) {
- if (entry->attachment.interface == intf) {
- adf_device_detach_op(dev,
- entry->attachment.overlay_engine, intf);
- adf_attachment_free(entry);
- dev->n_attached--;
- }
- }
- kfree(intf->modelist);
- adf_interface_sysfs_destroy(intf);
- adf_obj_destroy(&intf->base, &dev->interfaces);
- dev->n_interfaces--;
- mutex_unlock(&dev->client_lock);
- }
- EXPORT_SYMBOL(adf_interface_destroy);
- static bool adf_overlay_engine_has_custom_formats(
- const struct adf_overlay_engine_ops *ops)
- {
- size_t i;
- for (i = 0; i < ops->n_supported_formats; i++)
- if (!adf_format_is_standard(ops->supported_formats[i]))
- return true;
- return false;
- }
- /**
- * adf_overlay_engine_init - initialize ADF-internal data for an
- * overlay engine and create sysfs entries
- *
- * @eng: the overlay engine
- * @dev: the overlay engine's "parent" display device
- * @ops: the overlay engine's associated ops
- * @fmt: formatting string for the overlay engine's name
- *
- * @dev must have previously been initialized with adf_device_init().
- *
- * @fmt affects the name returned to userspace through the
- * %ADF_GET_OVERLAY_ENGINE_DATA ioctl. It does not affect the sysfs filename,
- * which is derived from @dev's name.
- *
- * Returns 0 on success or error code (<0) on failure.
- */
- int adf_overlay_engine_init(struct adf_overlay_engine *eng,
- struct adf_device *dev,
- const struct adf_overlay_engine_ops *ops, const char *fmt, ...)
- {
- int ret;
- va_list args;
- if (!ops->supported_formats) {
- pr_err("%s: overlay engine must support at least one format\n",
- __func__);
- return -EINVAL;
- }
- if (ops->n_supported_formats > ADF_MAX_SUPPORTED_FORMATS) {
- pr_err("%s: overlay engine supports too many formats\n",
- __func__);
- return -EINVAL;
- }
- if (adf_overlay_engine_has_custom_formats(ops) &&
- !dev->ops->validate_custom_format) {
- pr_err("%s: overlay engine has custom formats but parent device %s does not implement validate_custom_format\n",
- __func__, dev->base.name);
- return -EINVAL;
- }
- memset(eng, 0, sizeof(*eng));
- va_start(args, fmt);
- ret = adf_obj_init(&eng->base, ADF_OBJ_OVERLAY_ENGINE,
- &dev->overlay_engines, dev, &ops->base, fmt, args);
- va_end(args);
- if (ret < 0)
- return ret;
- eng->ops = ops;
- ret = adf_overlay_engine_sysfs_init(eng);
- if (ret < 0)
- goto err;
- return 0;
- err:
- adf_obj_destroy(&eng->base, &dev->overlay_engines);
- return ret;
- }
- EXPORT_SYMBOL(adf_overlay_engine_init);
- /**
- * adf_interface_destroy - clean up ADF-internal data for an overlay engine
- *
- * @eng: the overlay engine
- */
- void adf_overlay_engine_destroy(struct adf_overlay_engine *eng)
- {
- struct adf_device *dev = adf_overlay_engine_parent(eng);
- struct adf_attachment_list *entry, *next;
- mutex_lock(&dev->client_lock);
- list_for_each_entry_safe(entry, next, &dev->attach_allowed, head) {
- if (entry->attachment.overlay_engine == eng) {
- adf_attachment_free(entry);
- dev->n_attach_allowed--;
- }
- }
- list_for_each_entry_safe(entry, next, &dev->attached, head) {
- if (entry->attachment.overlay_engine == eng) {
- adf_device_detach_op(dev, eng,
- entry->attachment.interface);
- adf_attachment_free(entry);
- dev->n_attached--;
- }
- }
- adf_overlay_engine_sysfs_destroy(eng);
- adf_obj_destroy(&eng->base, &dev->overlay_engines);
- mutex_unlock(&dev->client_lock);
- }
- EXPORT_SYMBOL(adf_overlay_engine_destroy);
- struct adf_attachment_list *adf_attachment_find(struct list_head *list,
- struct adf_overlay_engine *eng, struct adf_interface *intf)
- {
- struct adf_attachment_list *entry;
- list_for_each_entry(entry, list, head) {
- if (entry->attachment.interface == intf &&
- entry->attachment.overlay_engine == eng)
- return entry;
- }
- return NULL;
- }
- int adf_attachment_validate(struct adf_device *dev,
- struct adf_overlay_engine *eng, struct adf_interface *intf)
- {
- struct adf_device *intf_dev = adf_interface_parent(intf);
- struct adf_device *eng_dev = adf_overlay_engine_parent(eng);
- if (intf_dev != dev) {
- dev_err(&dev->base.dev, "can't attach interface %s belonging to device %s\n",
- intf->base.name, intf_dev->base.name);
- return -EINVAL;
- }
- if (eng_dev != dev) {
- dev_err(&dev->base.dev, "can't attach overlay engine %s belonging to device %s\n",
- eng->base.name, eng_dev->base.name);
- return -EINVAL;
- }
- return 0;
- }
- /**
- * adf_attachment_allow - add a new entry to the list of allowed
- * attachments
- *
- * @dev: the parent device
- * @eng: the overlay engine
- * @intf: the interface
- *
- * adf_attachment_allow() indicates that the underlying display hardware allows
- * @intf to scan out @eng's output. It is intended to be called at
- * driver initialization for each supported overlay engine + interface pair.
- *
- * Returns 0 on success, -%EALREADY if the entry already exists, or -errno on
- * any other failure.
- */
- int adf_attachment_allow(struct adf_device *dev,
- struct adf_overlay_engine *eng, struct adf_interface *intf)
- {
- int ret;
- struct adf_attachment_list *entry = NULL;
- ret = adf_attachment_validate(dev, eng, intf);
- if (ret < 0)
- return ret;
- mutex_lock(&dev->client_lock);
- if (dev->n_attach_allowed == ADF_MAX_ATTACHMENTS) {
- ret = -ENOMEM;
- goto done;
- }
- if (adf_attachment_find(&dev->attach_allowed, eng, intf)) {
- ret = -EALREADY;
- goto done;
- }
- entry = kzalloc(sizeof(*entry), GFP_KERNEL);
- if (!entry) {
- ret = -ENOMEM;
- goto done;
- }
- entry->attachment.interface = intf;
- entry->attachment.overlay_engine = eng;
- list_add_tail(&entry->head, &dev->attach_allowed);
- dev->n_attach_allowed++;
- done:
- mutex_unlock(&dev->client_lock);
- if (ret < 0)
- kfree(entry);
- return ret;
- }
- EXPORT_SYMBOL(adf_attachment_allow);
- /**
- * adf_obj_type_str - string representation of an adf_obj_type
- *
- * @type: the object type
- */
- const char *adf_obj_type_str(enum adf_obj_type type)
- {
- switch (type) {
- case ADF_OBJ_OVERLAY_ENGINE:
- return "overlay engine";
- case ADF_OBJ_INTERFACE:
- return "interface";
- case ADF_OBJ_DEVICE:
- return "device";
- default:
- return "unknown";
- }
- }
- EXPORT_SYMBOL(adf_obj_type_str);
- /**
- * adf_interface_type_str - string representation of an adf_interface's type
- *
- * @intf: the interface
- */
- const char *adf_interface_type_str(struct adf_interface *intf)
- {
- switch (intf->type) {
- case ADF_INTF_DSI:
- return "DSI";
- case ADF_INTF_eDP:
- return "eDP";
- case ADF_INTF_DPI:
- return "DPI";
- case ADF_INTF_VGA:
- return "VGA";
- case ADF_INTF_DVI:
- return "DVI";
- case ADF_INTF_HDMI:
- return "HDMI";
- case ADF_INTF_MEMORY:
- return "memory";
- default:
- if (intf->type >= ADF_INTF_TYPE_DEVICE_CUSTOM) {
- if (intf->ops && intf->ops->type_str)
- return intf->ops->type_str(intf);
- return "custom";
- }
- return "unknown";
- }
- }
- EXPORT_SYMBOL(adf_interface_type_str);
- /**
- * adf_event_type_str - string representation of an adf_event_type
- *
- * @obj: ADF object that produced the event
- * @type: event type
- */
- const char *adf_event_type_str(struct adf_obj *obj, enum adf_event_type type)
- {
- switch (type) {
- case ADF_EVENT_VSYNC:
- return "vsync";
- case ADF_EVENT_HOTPLUG:
- return "hotplug";
- default:
- if (type >= ADF_EVENT_DEVICE_CUSTOM) {
- if (obj->ops && obj->ops->event_type_str)
- return obj->ops->event_type_str(obj, type);
- return "custom";
- }
- return "unknown";
- }
- }
- EXPORT_SYMBOL(adf_event_type_str);
- /**
- * adf_format_str - string representation of an ADF/DRM fourcc format
- *
- * @format: format fourcc
- * @buf: target buffer for the format's string representation
- */
- void adf_format_str(u32 format, char buf[ADF_FORMAT_STR_SIZE])
- {
- buf[0] = format & 0xFF;
- buf[1] = (format >> 8) & 0xFF;
- buf[2] = (format >> 16) & 0xFF;
- buf[3] = (format >> 24) & 0xFF;
- buf[4] = '\0';
- }
- EXPORT_SYMBOL(adf_format_str);
- /**
- * adf_format_validate_yuv - validate the number and size of planes in buffers
- * with a custom YUV format.
- *
- * @dev: ADF device performing the validation
- * @buf: buffer to validate
- * @num_planes: expected number of planes
- * @hsub: expected horizontal chroma subsampling factor, in pixels
- * @vsub: expected vertical chroma subsampling factor, in pixels
- * @cpp: expected bytes per pixel for each plane (length @num_planes)
- *
- * adf_format_validate_yuv() is intended to be called as a helper from @dev's
- * validate_custom_format() op.
- *
- * Returns 0 if @buf has the expected number of planes and each plane
- * has sufficient size, or -EINVAL otherwise.
- */
- int adf_format_validate_yuv(struct adf_device *dev, struct adf_buffer *buf,
- u8 num_planes, u8 hsub, u8 vsub, u8 cpp[])
- {
- u8 i;
- if (num_planes != buf->n_planes) {
- char format_str[ADF_FORMAT_STR_SIZE];
- adf_format_str(buf->format, format_str);
- dev_err(&dev->base.dev, "%u planes expected for format %s but %u planes provided\n",
- num_planes, format_str, buf->n_planes);
- return -EINVAL;
- }
- if (buf->w == 0 || buf->w % hsub) {
- dev_err(&dev->base.dev, "bad buffer width %u\n", buf->w);
- return -EINVAL;
- }
- if (buf->h == 0 || buf->h % vsub) {
- dev_err(&dev->base.dev, "bad buffer height %u\n", buf->h);
- return -EINVAL;
- }
- for (i = 0; i < num_planes; i++) {
- u32 width = buf->w / (i != 0 ? hsub : 1);
- u32 height = buf->h / (i != 0 ? vsub : 1);
- u8 cpp = adf_format_plane_cpp(buf->format, i);
- u32 last_line_size;
- if (buf->pitch[i] < (u64) width * cpp) {
- dev_err(&dev->base.dev, "plane %u pitch is shorter than buffer width (pitch = %u, width = %u, bpp = %u)\n",
- i, buf->pitch[i], width, cpp * 8);
- return -EINVAL;
- }
- switch (dev->ops->quirks.buffer_padding) {
- case ADF_BUFFER_PADDED_TO_PITCH:
- last_line_size = buf->pitch[i];
- break;
- case ADF_BUFFER_UNPADDED:
- last_line_size = width * cpp;
- break;
- default:
- BUG();
- }
- if ((u64) (height - 1) * buf->pitch[i] + last_line_size +
- buf->offset[i] > buf->dma_bufs[i]->size) {
- dev_err(&dev->base.dev, "plane %u buffer too small (height = %u, pitch = %u, offset = %u, size = %zu)\n",
- i, height, buf->pitch[i],
- buf->offset[i], buf->dma_bufs[i]->size);
- return -EINVAL;
- }
- }
- return 0;
- }
- EXPORT_SYMBOL(adf_format_validate_yuv);
- /**
- * adf_modeinfo_set_name - sets the name of a mode from its display resolution
- *
- * @mode: mode
- *
- * adf_modeinfo_set_name() fills in @mode->name in the format
- * "[hdisplay]x[vdisplay](i)". It is intended to help drivers create
- * ADF/DRM-style modelists from other mode formats.
- */
- void adf_modeinfo_set_name(struct drm_mode_modeinfo *mode)
- {
- bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
- snprintf(mode->name, DRM_DISPLAY_MODE_LEN, "%dx%d%s",
- mode->hdisplay, mode->vdisplay,
- interlaced ? "i" : "");
- }
- EXPORT_SYMBOL(adf_modeinfo_set_name);
- /**
- * adf_modeinfo_set_vrefresh - sets the vrefresh of a mode from its other
- * timing data
- *
- * @mode: mode
- *
- * adf_modeinfo_set_vrefresh() calculates @mode->vrefresh from
- * @mode->{h,v}display and @mode->flags. It is intended to help drivers
- * create ADF/DRM-style modelists from other mode formats.
- */
- void adf_modeinfo_set_vrefresh(struct drm_mode_modeinfo *mode)
- {
- int refresh = 0;
- unsigned int calc_val;
- if (mode->vrefresh > 0)
- return;
- if (mode->htotal <= 0 || mode->vtotal <= 0)
- return;
- /* work out vrefresh the value will be x1000 */
- calc_val = (mode->clock * 1000);
- calc_val /= mode->htotal;
- refresh = (calc_val + mode->vtotal / 2) / mode->vtotal;
- if (mode->flags & DRM_MODE_FLAG_INTERLACE)
- refresh *= 2;
- if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
- refresh /= 2;
- if (mode->vscan > 1)
- refresh /= mode->vscan;
- mode->vrefresh = refresh;
- }
- EXPORT_SYMBOL(adf_modeinfo_set_vrefresh);
- static int __init adf_init(void)
- {
- int err;
- err = adf_sysfs_init();
- if (err < 0)
- return err;
- return 0;
- }
- static void __exit adf_exit(void)
- {
- adf_sysfs_destroy();
- }
- module_init(adf_init);
- module_exit(adf_exit);
|