2 * drivers/video/tegra/dc/ext/events.c
4 * Copyright (c) 2011-2012, NVIDIA CORPORATION, All rights reserved.
6 * Author: Robert Morell <rmorell@nvidia.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
19 #include <linux/err.h>
21 #include <linux/list.h>
22 #include <linux/poll.h>
23 #include <linux/sched.h>
24 #include <linux/slab.h>
25 #include <linux/uaccess.h>
27 #include "tegra_dc_ext_priv.h"
29 static DECLARE_WAIT_QUEUE_HEAD(event_wait);
31 unsigned int tegra_dc_ext_event_poll(struct file *filp, poll_table *wait)
33 struct tegra_dc_ext_control_user *user = filp->private_data;
34 unsigned int mask = 0;
36 poll_wait(filp, &event_wait, wait);
38 if (atomic_read(&user->num_events))
44 static int get_next_event(struct tegra_dc_ext_control_user *user,
45 struct tegra_dc_ext_event_list *event,
48 struct list_head *list = &user->event_list;
49 struct tegra_dc_ext_event_list *next_event;
53 ret = wait_event_interruptible(event_wait,
54 atomic_read(&user->num_events));
57 if (ret == -ERESTARTSYS)
62 if (!atomic_read(&user->num_events))
66 mutex_lock(&user->lock);
68 BUG_ON(list_empty(list));
69 next_event = list_first_entry(list, struct tegra_dc_ext_event_list,
72 list_del(&next_event->list);
75 atomic_dec(&user->num_events);
77 mutex_unlock(&user->lock);
82 ssize_t tegra_dc_ext_event_read(struct file *filp, char __user *buf,
83 size_t size, loff_t *ppos)
85 struct tegra_dc_ext_control_user *user = filp->private_data;
86 struct tegra_dc_ext_event_list event_elem;
87 struct tegra_dc_ext_event *event = &event_elem.event;
88 ssize_t retval = 0, to_copy, event_size, pending;
89 loff_t previously_copied = 0;
95 if (user->partial_copy) {
97 * We didn't transfer the entire event last time, need to
100 event_elem = user->event_to_copy;
101 previously_copied = user->partial_copy;
103 /* Get the next event, if any */
104 pending = get_next_event(user, &event_elem,
105 !(filp->f_flags & O_NONBLOCK));
110 /* Write the event to the user */
111 event_size = sizeof(*event) + event->data_size;
112 BUG_ON(event_size <= previously_copied);
113 event_size -= previously_copied;
115 to_copy_ptr = (char *)event + previously_copied;
116 to_copy = min_t(ssize_t, size, event_size);
117 if (copy_to_user(buf, to_copy_ptr, to_copy)) {
122 /* Note that we currently only deliver one event at a time */
124 if (event_size > to_copy) {
126 * We were only able to copy part of this event. Stash it for
129 user->event_to_copy = event_elem;
130 user->partial_copy = previously_copied + to_copy;
132 user->partial_copy = 0;
135 return to_copy ? to_copy : retval;
138 static int tegra_dc_ext_queue_event(struct tegra_dc_ext_control *control,
139 struct tegra_dc_ext_event *event)
141 struct list_head *cur;
144 mutex_lock(&control->lock);
145 list_for_each(cur, &control->users) {
146 struct tegra_dc_ext_control_user *user;
147 struct tegra_dc_ext_event_list *ev_list;
149 user = container_of(cur, struct tegra_dc_ext_control_user,
151 mutex_lock(&user->lock);
153 if (!(user->event_mask & event->type)) {
154 mutex_unlock(&user->lock);
158 ev_list = kmalloc(sizeof(*ev_list), GFP_KERNEL);
161 mutex_unlock(&user->lock);
165 memcpy(&ev_list->event, event,
166 sizeof(*event) + event->data_size);
168 list_add_tail(&ev_list->list, &user->event_list);
170 atomic_inc(&user->num_events);
172 mutex_unlock(&user->lock);
174 mutex_unlock(&control->lock);
176 /* Is it worth it to track waiters with more granularity? */
177 wake_up(&event_wait);
182 int tegra_dc_ext_queue_hotplug(struct tegra_dc_ext_control *control, int output)
185 struct tegra_dc_ext_event event;
186 struct tegra_dc_ext_control_event_hotplug hotplug;
189 pack.event.type = TEGRA_DC_EXT_EVENT_HOTPLUG;
190 pack.event.data_size = sizeof(pack.hotplug);
192 pack.hotplug.handle = output;
194 tegra_dc_ext_queue_event(control, &pack.event);