aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/tegra/dc/ext
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/tegra/dc/ext')
-rw-r--r--drivers/video/tegra/dc/ext/Makefile5
-rw-r--r--drivers/video/tegra/dc/ext/control.c279
-rw-r--r--drivers/video/tegra/dc/ext/cursor.c203
-rw-r--r--drivers/video/tegra/dc/ext/dev.c975
-rw-r--r--drivers/video/tegra/dc/ext/events.c197
-rw-r--r--drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h147
-rw-r--r--drivers/video/tegra/dc/ext/util.c78
7 files changed, 1884 insertions, 0 deletions
diff --git a/drivers/video/tegra/dc/ext/Makefile b/drivers/video/tegra/dc/ext/Makefile
new file mode 100644
index 00000000000..19860ab5db1
--- /dev/null
+++ b/drivers/video/tegra/dc/ext/Makefile
@@ -0,0 +1,5 @@
1obj-y += dev.o
2obj-y += util.o
3obj-y += cursor.o
4obj-y += events.o
5obj-y += control.o
diff --git a/drivers/video/tegra/dc/ext/control.c b/drivers/video/tegra/dc/ext/control.c
new file mode 100644
index 00000000000..9caf3e11c16
--- /dev/null
+++ b/drivers/video/tegra/dc/ext/control.c
@@ -0,0 +1,279 @@
1/*
2 * drivers/video/tegra/dc/ext/control.c
3 *
4 * Copyright (C) 2011, NVIDIA Corporation
5 *
6 * Author: Robert Morell <rmorell@nvidia.com>
7 *
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.
12 *
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
16 * more details.
17 */
18
19#include <linux/device.h>
20#include <linux/err.h>
21#include <linux/file.h>
22#include <linux/fs.h>
23#include <linux/slab.h>
24#include <linux/module.h>
25#include <linux/uaccess.h>
26
27#include "tegra_dc_ext_priv.h"
28
29static struct tegra_dc_ext_control g_control;
30
31int tegra_dc_ext_process_hotplug(int output)
32{
33 return tegra_dc_ext_queue_hotplug(&g_control, output);
34}
35
36static int
37get_output_properties(struct tegra_dc_ext_control_output_properties *properties)
38{
39 struct tegra_dc *dc;
40
41 /* TODO: this should be more dynamic */
42 if (properties->handle > 2)
43 return -EINVAL;
44
45 switch (properties->handle) {
46 case 0:
47 properties->type = TEGRA_DC_EXT_LVDS;
48 break;
49 case 1:
50 properties->type = TEGRA_DC_EXT_HDMI;
51 break;
52 default:
53 return -EINVAL;
54 }
55
56 properties->associated_head = properties->handle;
57 properties->head_mask = (1 << properties->associated_head);
58
59 dc = tegra_dc_get_dc(properties->associated_head);
60 properties->connected = tegra_dc_get_connected(dc);
61
62 return 0;
63}
64
65static int get_output_edid(struct tegra_dc_ext_control_output_edid *edid)
66{
67 struct tegra_dc *dc;
68 size_t user_size = edid->size;
69 struct tegra_dc_edid *dc_edid = NULL;
70 int ret;
71
72 /* TODO: this should be more dynamic */
73 if (edid->handle > 2)
74 return -EINVAL;
75
76 dc = tegra_dc_get_dc(edid->handle);
77
78 dc_edid = tegra_dc_get_edid(dc);
79 if (IS_ERR(dc_edid))
80 return PTR_ERR(dc_edid);
81
82 if (!dc_edid) {
83 edid->size = 0;
84 } else {
85 edid->size = dc_edid->len;
86
87 if (user_size < edid->size) {
88 ret = -EFBIG;
89 goto done;
90 }
91
92 if (copy_to_user(edid->data, dc_edid->buf, edid->size)) {
93 ret = -EFAULT;
94 goto done;
95 }
96
97 }
98
99done:
100 if (dc_edid)
101 tegra_dc_put_edid(dc_edid);
102
103 return ret;
104}
105
106static int set_event_mask(struct tegra_dc_ext_control_user *user, u32 mask)
107{
108 struct list_head *list, *tmp;
109
110 if (mask & ~TEGRA_DC_EXT_EVENT_MASK_ALL)
111 return -EINVAL;
112
113 mutex_lock(&user->lock);
114
115 user->event_mask = mask;
116
117 list_for_each_safe(list, tmp, &user->event_list) {
118 struct tegra_dc_ext_event_list *ev_list;
119 ev_list = list_entry(list, struct tegra_dc_ext_event_list,
120 list);
121 if (!(mask & ev_list->event.type)) {
122 list_del(list);
123 kfree(ev_list);
124 }
125 }
126 mutex_unlock(&user->lock);
127
128 return 0;
129}
130
131static int get_capabilities(struct tegra_dc_ext_control_capabilities *caps)
132{
133 caps->caps = TEGRA_DC_EXT_CAPABILITIES;
134 return 0;
135}
136
137static long tegra_dc_ext_control_ioctl(struct file *filp, unsigned int cmd,
138 unsigned long arg)
139{
140 void __user *user_arg = (void __user *)arg;
141 struct tegra_dc_ext_control_user *user = filp->private_data;
142
143 switch (cmd) {
144 case TEGRA_DC_EXT_CONTROL_GET_NUM_OUTPUTS:
145 {
146 u32 num = tegra_dc_ext_get_num_outputs();
147
148 if (copy_to_user(user_arg, &num, sizeof(num)))
149 return -EFAULT;
150
151 return 0;
152 }
153 case TEGRA_DC_EXT_CONTROL_GET_OUTPUT_PROPERTIES:
154 {
155 struct tegra_dc_ext_control_output_properties args;
156 int ret;
157
158 if (copy_from_user(&args, user_arg, sizeof(args)))
159 return -EFAULT;
160
161 ret = get_output_properties(&args);
162
163 if (copy_to_user(user_arg, &args, sizeof(args)))
164 return -EFAULT;
165
166 return ret;
167 }
168 case TEGRA_DC_EXT_CONTROL_GET_OUTPUT_EDID:
169 {
170 struct tegra_dc_ext_control_output_edid args;
171 int ret;
172
173 if (copy_from_user(&args, user_arg, sizeof(args)))
174 return -EFAULT;
175
176 ret = get_output_edid(&args);
177
178 if (copy_to_user(user_arg, &args, sizeof(args)))
179 return -EFAULT;
180
181 return ret;
182 }
183 case TEGRA_DC_EXT_CONTROL_SET_EVENT_MASK:
184 return set_event_mask(user, (u32) arg);
185 case TEGRA_DC_EXT_CONTROL_GET_CAPABILITIES:
186 {
187 struct tegra_dc_ext_control_capabilities args;
188 int ret;
189
190 ret = get_capabilities(&args);
191
192 if (copy_to_user(user_arg, &args, sizeof(args)))
193 return -EFAULT;
194
195 return ret;
196 }
197 default:
198 return -EINVAL;
199 }
200}
201
202static int tegra_dc_ext_control_open(struct inode *inode, struct file *filp)
203{
204 struct tegra_dc_ext_control_user *user;
205 struct tegra_dc_ext_control *control;
206
207 user = kzalloc(sizeof(*user), GFP_KERNEL);
208 if (!user)
209 return -ENOMEM;
210
211 control = container_of(inode->i_cdev, struct tegra_dc_ext_control,
212 cdev);
213 user->control = control;;
214
215 INIT_LIST_HEAD(&user->event_list);
216 mutex_init(&user->lock);
217
218 filp->private_data = user;
219
220 mutex_lock(&control->lock);
221 list_add(&user->list, &control->users);
222 mutex_unlock(&control->lock);
223
224 return 0;
225}
226
227static int tegra_dc_ext_control_release(struct inode *inode, struct file *filp)
228{
229 struct tegra_dc_ext_control_user *user = filp->private_data;
230 struct tegra_dc_ext_control *control = user->control;
231
232 /* This will free any pending events for this user */
233 set_event_mask(user, 0);
234
235 mutex_lock(&control->lock);
236 list_del(&user->list);
237 mutex_unlock(&control->lock);
238
239 kfree(user);
240
241 return 0;
242}
243
244static const struct file_operations tegra_dc_ext_event_devops = {
245 .owner = THIS_MODULE,
246 .open = tegra_dc_ext_control_open,
247 .release = tegra_dc_ext_control_release,
248 .read = tegra_dc_ext_event_read,
249 .poll = tegra_dc_ext_event_poll,
250 .unlocked_ioctl = tegra_dc_ext_control_ioctl,
251};
252
253int tegra_dc_ext_control_init(void)
254{
255 struct tegra_dc_ext_control *control = &g_control;
256 int ret;
257
258 cdev_init(&control->cdev, &tegra_dc_ext_event_devops);
259 control->cdev.owner = THIS_MODULE;
260 ret = cdev_add(&control->cdev, tegra_dc_ext_devno, 1);
261 if (ret)
262 return ret;
263
264 control->dev = device_create(tegra_dc_ext_class,
265 NULL,
266 tegra_dc_ext_devno,
267 NULL,
268 "tegra_dc_ctrl");
269 if (IS_ERR(control->dev)) {
270 ret = PTR_ERR(control->dev);
271 cdev_del(&control->cdev);
272 }
273
274 mutex_init(&control->lock);
275
276 INIT_LIST_HEAD(&control->users);
277
278 return ret;
279}
diff --git a/drivers/video/tegra/dc/ext/cursor.c b/drivers/video/tegra/dc/ext/cursor.c
new file mode 100644
index 00000000000..d8fa5fd8e6d
--- /dev/null
+++ b/drivers/video/tegra/dc/ext/cursor.c
@@ -0,0 +1,203 @@
1/*
2 * drivers/video/tegra/dc/ext/cursor.c
3 *
4 * Copyright (C) 2011, NVIDIA Corporation
5 *
6 * Author: Robert Morell <rmorell@nvidia.com>
7 *
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.
12 *
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
16 * more details.
17 */
18
19#include <video/tegra_dc_ext.h>
20
21#include "tegra_dc_ext_priv.h"
22
23/* ugh */
24#include "../dc_priv.h"
25#include "../dc_reg.h"
26
27int tegra_dc_ext_get_cursor(struct tegra_dc_ext_user *user)
28{
29 struct tegra_dc_ext *ext = user->ext;
30 int ret = 0;
31
32 mutex_lock(&ext->cursor.lock);
33
34 if (!ext->cursor.user)
35 ext->cursor.user = user;
36 else if (ext->cursor.user != user)
37 ret = -EBUSY;
38
39 mutex_unlock(&ext->cursor.lock);
40
41 return ret;
42}
43
44int tegra_dc_ext_put_cursor(struct tegra_dc_ext_user *user)
45{
46 struct tegra_dc_ext *ext = user->ext;
47 int ret = 0;
48
49 mutex_lock(&ext->cursor.lock);
50
51 if (ext->cursor.user == user)
52 ext->cursor.user = 0;
53 else
54 ret = -EACCES;
55
56 mutex_unlock(&ext->cursor.lock);
57
58 return ret;
59}
60
61static void set_cursor_image_hw(struct tegra_dc *dc,
62 struct tegra_dc_ext_cursor_image *args,
63 dma_addr_t phys_addr)
64{
65 tegra_dc_writel(dc,
66 CURSOR_COLOR(args->foreground.r,
67 args->foreground.g,
68 args->foreground.b),
69 DC_DISP_CURSOR_FOREGROUND);
70 tegra_dc_writel(dc,
71 CURSOR_COLOR(args->background.r,
72 args->background.g,
73 args->background.b),
74 DC_DISP_CURSOR_BACKGROUND);
75
76 BUG_ON(phys_addr & ~CURSOR_START_ADDR_MASK);
77
78 tegra_dc_writel(dc,
79 CURSOR_START_ADDR(((unsigned long) phys_addr)) |
80 ((args->flags & TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_64x64) ?
81 CURSOR_SIZE_64 : 0),
82 DC_DISP_CURSOR_START_ADDR);
83}
84
85int tegra_dc_ext_set_cursor_image(struct tegra_dc_ext_user *user,
86 struct tegra_dc_ext_cursor_image *args)
87{
88 struct tegra_dc_ext *ext = user->ext;
89 struct tegra_dc *dc = ext->dc;
90 struct nvmap_handle_ref *handle, *old_handle;
91 dma_addr_t phys_addr;
92 u32 size;
93 int ret;
94
95 if (!user->nvmap)
96 return -EFAULT;
97
98 size = args->flags & (TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_32x32 |
99 TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_64x64);
100
101 if (size != TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_32x32 &&
102 size != TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_64x64)
103 return -EINVAL;
104
105 mutex_lock(&ext->cursor.lock);
106
107 if (ext->cursor.user != user) {
108 ret = -EACCES;
109 goto unlock;
110 }
111
112 if (!ext->enabled) {
113 ret = -ENXIO;
114 goto unlock;
115 }
116
117 old_handle = ext->cursor.cur_handle;
118
119 ret = tegra_dc_ext_pin_window(user, args->buff_id, &handle, &phys_addr);
120 if (ret)
121 goto unlock;
122
123 ext->cursor.cur_handle = handle;
124
125 mutex_lock(&dc->lock);
126
127 set_cursor_image_hw(dc, args, phys_addr);
128
129 tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
130 tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
131
132 /* XXX sync here? */
133
134 mutex_unlock(&dc->lock);
135
136 mutex_unlock(&ext->cursor.lock);
137
138 if (old_handle) {
139 nvmap_unpin(ext->nvmap, old_handle);
140 nvmap_free(ext->nvmap, old_handle);
141 }
142
143 return 0;
144
145unlock:
146 mutex_unlock(&ext->cursor.lock);
147
148 return ret;
149}
150
151int tegra_dc_ext_set_cursor(struct tegra_dc_ext_user *user,
152 struct tegra_dc_ext_cursor *args)
153{
154 struct tegra_dc_ext *ext = user->ext;
155 struct tegra_dc *dc = ext->dc;
156 u32 win_options;
157 bool enable;
158 int ret;
159
160 mutex_lock(&ext->cursor.lock);
161
162 if (ext->cursor.user != user) {
163 ret = -EACCES;
164 goto unlock;
165 }
166
167 if (!ext->enabled) {
168 ret = -ENXIO;
169 goto unlock;
170 }
171
172 enable = !!(args->flags & TEGRA_DC_EXT_CURSOR_FLAGS_VISIBLE);
173
174 mutex_lock(&dc->lock);
175
176 win_options = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
177 if (!!(win_options & CURSOR_ENABLE) != enable) {
178 win_options &= ~CURSOR_ENABLE;
179 if (enable)
180 win_options |= CURSOR_ENABLE;
181 tegra_dc_writel(dc, win_options, DC_DISP_DISP_WIN_OPTIONS);
182 }
183
184 tegra_dc_writel(dc, CURSOR_POSITION(args->x, args->y),
185 DC_DISP_CURSOR_POSITION);
186
187 tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
188 tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
189
190 /* TODO: need to sync here? hopefully can avoid this, but need to
191 * figure out interaction w/ rest of GENERAL_ACT_REQ */
192
193 mutex_unlock(&dc->lock);
194
195 mutex_unlock(&ext->cursor.lock);
196
197 return 0;
198
199unlock:
200 mutex_unlock(&ext->cursor.lock);
201
202 return ret;
203}
diff --git a/drivers/video/tegra/dc/ext/dev.c b/drivers/video/tegra/dc/ext/dev.c
new file mode 100644
index 00000000000..04553e77839
--- /dev/null
+++ b/drivers/video/tegra/dc/ext/dev.c
@@ -0,0 +1,975 @@
1/*
2 * drivers/video/tegra/dc/dev.c
3 *
4 * Copyright (C) 2011-2012, NVIDIA Corporation
5 *
6 * Author: Robert Morell <rmorell@nvidia.com>
7 * Some code based on fbdev extensions written by:
8 * Erik Gilling <konkers@android.com>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18 * more details.
19 */
20
21#include <linux/file.h>
22#include <linux/fs.h>
23#include <linux/uaccess.h>
24#include <linux/slab.h>
25#include <linux/workqueue.h>
26
27#include <video/tegra_dc_ext.h>
28
29#include <mach/dc.h>
30#include <mach/nvmap.h>
31#include <mach/tegra_dc_ext.h>
32
33/* XXX ew */
34#include "../dc_priv.h"
35/* XXX ew 2 */
36#include "../../host/dev.h"
37/* XXX ew 3 */
38#include "../../nvmap/nvmap.h"
39#include "tegra_dc_ext_priv.h"
40
41int tegra_dc_ext_devno;
42struct class *tegra_dc_ext_class;
43static int head_count;
44
45struct tegra_dc_ext_flip_win {
46 struct tegra_dc_ext_flip_windowattr attr;
47 struct nvmap_handle_ref *handle[TEGRA_DC_NUM_PLANES];
48 dma_addr_t phys_addr;
49 dma_addr_t phys_addr_u;
50 dma_addr_t phys_addr_v;
51 u32 syncpt_max;
52};
53
54struct tegra_dc_ext_flip_data {
55 struct tegra_dc_ext *ext;
56 struct work_struct work;
57 struct tegra_dc_ext_flip_win win[DC_N_WINDOWS];
58};
59
60int tegra_dc_ext_get_num_outputs(void)
61{
62 /* TODO: decouple output count from head count */
63 return head_count;
64}
65
66static int tegra_dc_ext_set_nvmap_fd(struct tegra_dc_ext_user *user,
67 int fd)
68{
69 struct nvmap_client *nvmap = NULL;
70
71 if (fd >= 0) {
72 nvmap = nvmap_client_get_file(fd);
73 if (IS_ERR(nvmap))
74 return PTR_ERR(nvmap);
75 }
76
77 if (user->nvmap)
78 nvmap_client_put(user->nvmap);
79
80 user->nvmap = nvmap;
81
82 return 0;
83}
84
85static int tegra_dc_ext_get_window(struct tegra_dc_ext_user *user,
86 unsigned int n)
87{
88 struct tegra_dc_ext *ext = user->ext;
89 struct tegra_dc_ext_win *win;
90 int ret = 0;
91
92 if (n >= DC_N_WINDOWS)
93 return -EINVAL;
94
95 win = &ext->win[n];
96
97 mutex_lock(&win->lock);
98
99 if (!win->user)
100 win->user = user;
101 else if (win->user != user)
102 ret = -EBUSY;
103
104 mutex_unlock(&win->lock);
105
106 return ret;
107}
108
109static int tegra_dc_ext_put_window(struct tegra_dc_ext_user *user,
110 unsigned int n)
111{
112 struct tegra_dc_ext *ext = user->ext;
113 struct tegra_dc_ext_win *win;
114 int ret = 0;
115
116 if (n >= DC_N_WINDOWS)
117 return -EINVAL;
118
119 win = &ext->win[n];
120
121 mutex_lock(&win->lock);
122
123 if (win->user == user) {
124 flush_workqueue(win->flip_wq);
125 win->user = 0;
126 } else {
127 ret = -EACCES;
128 }
129
130 mutex_unlock(&win->lock);
131
132 return ret;
133}
134
135static void set_enable(struct tegra_dc_ext *ext, bool en)
136{
137 int i;
138
139 /*
140 * Take all locks to make sure any flip requests or cursor moves are
141 * out of their critical sections
142 */
143 for (i = 0; i < ext->dc->n_windows; i++)
144 mutex_lock(&ext->win[i].lock);
145 mutex_lock(&ext->cursor.lock);
146
147 ext->enabled = en;
148
149 mutex_unlock(&ext->cursor.lock);
150 for (i = ext->dc->n_windows - 1; i >= 0 ; i--)
151 mutex_unlock(&ext->win[i].lock);
152}
153
154void tegra_dc_ext_enable(struct tegra_dc_ext *ext)
155{
156 set_enable(ext, true);
157}
158
159void tegra_dc_ext_disable(struct tegra_dc_ext *ext)
160{
161 int i;
162 set_enable(ext, false);
163
164 /*
165 * Flush the flip queue -- note that this must be called with dc->lock
166 * unlocked or else it will hang.
167 */
168 for (i = 0; i < ext->dc->n_windows; i++) {
169 struct tegra_dc_ext_win *win = &ext->win[i];
170
171 flush_workqueue(win->flip_wq);
172 }
173}
174
175static int tegra_dc_ext_set_windowattr(struct tegra_dc_ext *ext,
176 struct tegra_dc_win *win,
177 const struct tegra_dc_ext_flip_win *flip_win)
178{
179 struct tegra_dc_ext_win *ext_win = &ext->win[win->idx];
180
181 if (flip_win->handle[TEGRA_DC_Y] == NULL) {
182 win->flags = 0;
183 memset(ext_win->cur_handle, 0, sizeof(ext_win->cur_handle));
184 return 0;
185 }
186
187 win->flags = TEGRA_WIN_FLAG_ENABLED;
188 if (flip_win->attr.blend == TEGRA_DC_EXT_BLEND_PREMULT)
189 win->flags |= TEGRA_WIN_FLAG_BLEND_PREMULT;
190 else if (flip_win->attr.blend == TEGRA_DC_EXT_BLEND_COVERAGE)
191 win->flags |= TEGRA_WIN_FLAG_BLEND_COVERAGE;
192 if (flip_win->attr.flags & TEGRA_DC_EXT_FLIP_FLAG_TILED)
193 win->flags |= TEGRA_WIN_FLAG_TILED;
194 if (flip_win->attr.flags & TEGRA_DC_EXT_FLIP_FLAG_INVERT_H)
195 win->flags |= TEGRA_WIN_FLAG_INVERT_H;
196 if (flip_win->attr.flags & TEGRA_DC_EXT_FLIP_FLAG_INVERT_V)
197 win->flags |= TEGRA_WIN_FLAG_INVERT_V;
198 win->fmt = flip_win->attr.pixformat;
199 win->x.full = flip_win->attr.x;
200 win->y.full = flip_win->attr.y;
201 win->w.full = flip_win->attr.w;
202 win->h.full = flip_win->attr.h;
203 /* XXX verify that this doesn't go outside display's active region */
204 win->out_x = flip_win->attr.out_x;
205 win->out_y = flip_win->attr.out_y;
206 win->out_w = flip_win->attr.out_w;
207 win->out_h = flip_win->attr.out_h;
208 win->z = flip_win->attr.z;
209 memcpy(ext_win->cur_handle, flip_win->handle,
210 sizeof(ext_win->cur_handle));
211
212 /* XXX verify that this won't read outside of the surface */
213 win->phys_addr = flip_win->phys_addr + flip_win->attr.offset;
214
215 win->phys_addr_u = flip_win->handle[TEGRA_DC_U] ?
216 flip_win->phys_addr_u : flip_win->phys_addr;
217 win->phys_addr_u += flip_win->attr.offset_u;
218
219 win->phys_addr_v = flip_win->handle[TEGRA_DC_V] ?
220 flip_win->phys_addr_v : flip_win->phys_addr;
221 win->phys_addr_v += flip_win->attr.offset_v;
222
223 win->stride = flip_win->attr.stride;
224 win->stride_uv = flip_win->attr.stride_uv;
225
226 if ((s32)flip_win->attr.pre_syncpt_id >= 0) {
227 nvhost_syncpt_wait_timeout(
228 &nvhost_get_host(ext->dc->ndev)->syncpt,
229 flip_win->attr.pre_syncpt_id,
230 flip_win->attr.pre_syncpt_val,
231 msecs_to_jiffies(500), NULL);
232 }
233
234
235 return 0;
236}
237
238static void tegra_dc_ext_flip_worker(struct work_struct *work)
239{
240 struct tegra_dc_ext_flip_data *data =
241 container_of(work, struct tegra_dc_ext_flip_data, work);
242 struct tegra_dc_ext *ext = data->ext;
243 struct tegra_dc_win *wins[DC_N_WINDOWS];
244 struct nvmap_handle_ref *unpin_handles[DC_N_WINDOWS *
245 TEGRA_DC_NUM_PLANES];
246 struct nvmap_handle_ref *old_handle;
247 int i, nr_unpin = 0, nr_win = 0;
248 bool skip_flip = false;
249
250 for (i = 0; i < DC_N_WINDOWS; i++) {
251 struct tegra_dc_ext_flip_win *flip_win = &data->win[i];
252 int index = flip_win->attr.index;
253 struct tegra_dc_win *win;
254 struct tegra_dc_ext_win *ext_win;
255
256 if (index < 0)
257 continue;
258
259 win = tegra_dc_get_window(ext->dc, index);
260 ext_win = &ext->win[index];
261
262 if (!(atomic_dec_and_test(&ext_win->nr_pending_flips)) &&
263 (flip_win->attr.flags & TEGRA_DC_EXT_FLIP_FLAG_CURSOR))
264 skip_flip = true;
265
266 if (win->flags & TEGRA_WIN_FLAG_ENABLED) {
267 int j;
268 for (j = 0; j < TEGRA_DC_NUM_PLANES; j++) {
269 if (skip_flip)
270 old_handle = flip_win->handle[j];
271 else
272 old_handle = ext_win->cur_handle[j];
273
274 if (!old_handle)
275 continue;
276
277 unpin_handles[nr_unpin++] = old_handle;
278 }
279 }
280
281 if (!skip_flip)
282 tegra_dc_ext_set_windowattr(ext, win, &data->win[i]);
283
284 wins[nr_win++] = win;
285 }
286
287 if (!skip_flip) {
288 tegra_dc_update_windows(wins, nr_win);
289 /* TODO: implement swapinterval here */
290 tegra_dc_sync_windows(wins, nr_win);
291 }
292
293 for (i = 0; i < DC_N_WINDOWS; i++) {
294 struct tegra_dc_ext_flip_win *flip_win = &data->win[i];
295 int index = flip_win->attr.index;
296
297 if (index < 0)
298 continue;
299
300 tegra_dc_incr_syncpt_min(ext->dc, index,
301 flip_win->syncpt_max);
302 }
303
304 /* unpin and deref previous front buffers */
305 for (i = 0; i < nr_unpin; i++) {
306 nvmap_unpin(ext->nvmap, unpin_handles[i]);
307 nvmap_free(ext->nvmap, unpin_handles[i]);
308 }
309
310 kfree(data);
311}
312
313static int lock_windows_for_flip(struct tegra_dc_ext_user *user,
314 struct tegra_dc_ext_flip *args)
315{
316 struct tegra_dc_ext *ext = user->ext;
317 u8 idx_mask = 0;
318 int i;
319
320 for (i = 0; i < DC_N_WINDOWS; i++) {
321 int index = args->win[i].index;
322
323 if (index < 0)
324 continue;
325
326 idx_mask |= BIT(index);
327 }
328
329 for (i = 0; i < DC_N_WINDOWS; i++) {
330 struct tegra_dc_ext_win *win;
331
332 if (!(idx_mask & BIT(i)))
333 continue;
334
335 win = &ext->win[i];
336
337 mutex_lock(&win->lock);
338
339 if (win->user != user)
340 goto fail_unlock;
341 }
342
343 return 0;
344
345fail_unlock:
346 do {
347 if (!(idx_mask & BIT(i)))
348 continue;
349
350 mutex_unlock(&ext->win[i].lock);
351 } while (i--);
352
353 return -EACCES;
354}
355
356static void unlock_windows_for_flip(struct tegra_dc_ext_user *user,
357 struct tegra_dc_ext_flip *args)
358{
359 struct tegra_dc_ext *ext = user->ext;
360 u8 idx_mask = 0;
361 int i;
362
363 for (i = 0; i < DC_N_WINDOWS; i++) {
364 int index = args->win[i].index;
365
366 if (index < 0)
367 continue;
368
369 idx_mask |= BIT(index);
370 }
371
372 for (i = DC_N_WINDOWS - 1; i >= 0; i--) {
373 if (!(idx_mask & BIT(i)))
374 continue;
375
376 mutex_unlock(&ext->win[i].lock);
377 }
378}
379
380static int sanitize_flip_args(struct tegra_dc_ext_user *user,
381 struct tegra_dc_ext_flip *args)
382{
383 int i, used_windows = 0;
384
385 for (i = 0; i < DC_N_WINDOWS; i++) {
386 int index = args->win[i].index;
387
388 if (index < 0)
389 continue;
390
391 if (index >= DC_N_WINDOWS)
392 return -EINVAL;
393
394 if (used_windows & BIT(index))
395 return -EINVAL;
396
397 used_windows |= BIT(index);
398 }
399
400 if (!used_windows)
401 return -EINVAL;
402
403 return 0;
404}
405
406static int tegra_dc_ext_flip(struct tegra_dc_ext_user *user,
407 struct tegra_dc_ext_flip *args)
408{
409 struct tegra_dc_ext *ext = user->ext;
410 struct tegra_dc_ext_flip_data *data;
411 int work_index;
412 int i, ret = 0;
413
414#ifdef CONFIG_ANDROID
415 int index_check[DC_N_WINDOWS] = {0, };
416 int zero_index_id = 0;
417#endif
418
419 if (!user->nvmap)
420 return -EFAULT;
421
422 ret = sanitize_flip_args(user, args);
423 if (ret)
424 return ret;
425
426 data = kzalloc(sizeof(*data), GFP_KERNEL);
427 if (!data)
428 return -ENOMEM;
429
430 INIT_WORK(&data->work, tegra_dc_ext_flip_worker);
431 data->ext = ext;
432
433#ifdef CONFIG_ANDROID
434 for (i = 0; i < DC_N_WINDOWS; i++) {
435 index_check[i] = args->win[i].index;
436 if (index_check[i] == 0)
437 zero_index_id = i;
438 }
439
440 if (index_check[DC_N_WINDOWS - 1] != 0) {
441 struct tegra_dc_ext_flip_windowattr win_temp;
442 win_temp = args->win[DC_N_WINDOWS - 1];
443 args->win[DC_N_WINDOWS - 1] = args->win[zero_index_id];
444 args->win[zero_index_id] = win_temp;
445 }
446#endif
447
448 for (i = 0; i < DC_N_WINDOWS; i++) {
449 struct tegra_dc_ext_flip_win *flip_win = &data->win[i];
450 int index = args->win[i].index;
451
452 memcpy(&flip_win->attr, &args->win[i], sizeof(flip_win->attr));
453
454 if (index < 0)
455 continue;
456
457 ret = tegra_dc_ext_pin_window(user, flip_win->attr.buff_id,
458 &flip_win->handle[TEGRA_DC_Y],
459 &flip_win->phys_addr);
460 if (ret)
461 goto fail_pin;
462
463 if (flip_win->attr.buff_id_u) {
464 ret = tegra_dc_ext_pin_window(user,
465 flip_win->attr.buff_id_u,
466 &flip_win->handle[TEGRA_DC_U],
467 &flip_win->phys_addr_u);
468 if (ret)
469 goto fail_pin;
470 } else {
471 flip_win->handle[TEGRA_DC_U] = NULL;
472 flip_win->phys_addr_u = 0;
473 }
474
475 if (flip_win->attr.buff_id_v) {
476 ret = tegra_dc_ext_pin_window(user,
477 flip_win->attr.buff_id_v,
478 &flip_win->handle[TEGRA_DC_V],
479 &flip_win->phys_addr_v);
480 if (ret)
481 goto fail_pin;
482 } else {
483 flip_win->handle[TEGRA_DC_V] = NULL;
484 flip_win->phys_addr_v = 0;
485 }
486 }
487
488 ret = lock_windows_for_flip(user, args);
489 if (ret)
490 goto fail_pin;
491
492 if (!ext->enabled) {
493 ret = -ENXIO;
494 goto unlock;
495 }
496
497 for (i = 0; i < DC_N_WINDOWS; i++) {
498 u32 syncpt_max;
499 int index = args->win[i].index;
500 struct tegra_dc_win *win;
501 struct tegra_dc_ext_win *ext_win;
502
503 if (index < 0)
504 continue;
505
506 win = tegra_dc_get_window(ext->dc, index);
507 ext_win = &ext->win[index];
508
509 syncpt_max = tegra_dc_incr_syncpt_max(ext->dc, index);
510
511 data->win[i].syncpt_max = syncpt_max;
512
513 /*
514 * Any of these windows' syncpoints should be equivalent for
515 * the client, so we just send back an arbitrary one of them
516 */
517 args->post_syncpt_val = syncpt_max;
518 args->post_syncpt_id = tegra_dc_get_syncpt_id(ext->dc, index);
519 work_index = index;
520
521 atomic_inc(&ext->win[work_index].nr_pending_flips);
522 }
523 queue_work(ext->win[work_index].flip_wq, &data->work);
524
525 unlock_windows_for_flip(user, args);
526
527 return 0;
528
529unlock:
530 unlock_windows_for_flip(user, args);
531
532fail_pin:
533 for (i = 0; i < DC_N_WINDOWS; i++) {
534 int j;
535 for (j = 0; j < TEGRA_DC_NUM_PLANES; j++) {
536 if (!data->win[i].handle[j])
537 continue;
538
539 nvmap_unpin(ext->nvmap, data->win[i].handle[j]);
540 nvmap_free(ext->nvmap, data->win[i].handle[j]);
541 }
542 }
543 kfree(data);
544
545 return ret;
546}
547
548static int tegra_dc_ext_set_csc(struct tegra_dc_ext_user *user,
549 struct tegra_dc_ext_csc *new_csc)
550{
551 unsigned int index = new_csc->win_index;
552 struct tegra_dc *dc = user->ext->dc;
553 struct tegra_dc_ext_win *ext_win;
554 struct tegra_dc_csc *csc;
555
556 if (index >= DC_N_WINDOWS)
557 return -EINVAL;
558
559 ext_win = &user->ext->win[index];
560 csc = &dc->windows[index].csc;
561
562 mutex_lock(&ext_win->lock);
563
564 if (ext_win->user != user) {
565 mutex_unlock(&ext_win->lock);
566 return -EACCES;
567 }
568
569 csc->yof = new_csc->yof;
570 csc->kyrgb = new_csc->kyrgb;
571 csc->kur = new_csc->kur;
572 csc->kvr = new_csc->kvr;
573 csc->kug = new_csc->kug;
574 csc->kvg = new_csc->kvg;
575 csc->kub = new_csc->kub;
576 csc->kvb = new_csc->kvb;
577
578 tegra_dc_update_csc(dc, index);
579
580 mutex_unlock(&ext_win->lock);
581
582 return 0;
583}
584
585static int set_lut_channel(u16 *channel_from_user,
586 u8 *channel_to,
587 u32 start,
588 u32 len)
589{
590 int i;
591 u16 lut16bpp[256];
592
593 if (channel_from_user) {
594 if (copy_from_user(lut16bpp, channel_from_user, len<<1))
595 return 1;
596
597 for (i = 0; i < len; i++)
598 channel_to[start+i] = lut16bpp[i]>>8;
599 } else {
600 for (i = 0; i < len; i++)
601 channel_to[start+i] = start+i;
602 }
603
604 return 0;
605}
606
607static int tegra_dc_ext_set_lut(struct tegra_dc_ext_user *user,
608 struct tegra_dc_ext_lut *new_lut)
609{
610 int err;
611 unsigned int index = new_lut->win_index;
612 u32 start = new_lut->start;
613 u32 len = new_lut->len;
614
615 struct tegra_dc *dc = user->ext->dc;
616 struct tegra_dc_ext_win *ext_win;
617 struct tegra_dc_lut *lut;
618
619 if (index >= DC_N_WINDOWS)
620 return -EINVAL;
621
622 if ((start >= 256) || (len > 256) || ((start + len) > 256))
623 return -EINVAL;
624
625 ext_win = &user->ext->win[index];
626 lut = &dc->windows[index].lut;
627
628 mutex_lock(&ext_win->lock);
629
630 if (ext_win->user != user) {
631 mutex_unlock(&ext_win->lock);
632 return -EACCES;
633 }
634
635 err = set_lut_channel(new_lut->r, lut->r, start, len) |
636 set_lut_channel(new_lut->g, lut->g, start, len) |
637 set_lut_channel(new_lut->b, lut->b, start, len);
638
639 if (err) {
640 mutex_unlock(&ext_win->lock);
641 return -EFAULT;
642 }
643
644 tegra_dc_update_lut(dc, index,
645 new_lut->flags & TEGRA_DC_EXT_LUT_FLAGS_FBOVERRIDE);
646
647 mutex_unlock(&ext_win->lock);
648
649 return 0;
650}
651
652static u32 tegra_dc_ext_get_vblank_syncpt(struct tegra_dc_ext_user *user)
653{
654 struct tegra_dc *dc = user->ext->dc;
655
656 return dc->vblank_syncpt;
657}
658
659static int tegra_dc_ext_get_status(struct tegra_dc_ext_user *user,
660 struct tegra_dc_ext_status *status)
661{
662 struct tegra_dc *dc = user->ext->dc;
663
664 memset(status, 0, sizeof(*status));
665
666 if (dc->enabled)
667 status->flags |= TEGRA_DC_EXT_FLAGS_ENABLED;
668
669 return 0;
670}
671
672static long tegra_dc_ioctl(struct file *filp, unsigned int cmd,
673 unsigned long arg)
674{
675 void __user *user_arg = (void __user *)arg;
676 struct tegra_dc_ext_user *user = filp->private_data;
677
678 switch (cmd) {
679 case TEGRA_DC_EXT_SET_NVMAP_FD:
680 return tegra_dc_ext_set_nvmap_fd(user, arg);
681
682 case TEGRA_DC_EXT_GET_WINDOW:
683 return tegra_dc_ext_get_window(user, arg);
684 case TEGRA_DC_EXT_PUT_WINDOW:
685 return tegra_dc_ext_put_window(user, arg);
686
687 case TEGRA_DC_EXT_FLIP:
688 {
689 struct tegra_dc_ext_flip args;
690 int ret;
691
692 if (copy_from_user(&args, user_arg, sizeof(args)))
693 return -EFAULT;
694
695 ret = tegra_dc_ext_flip(user, &args);
696
697 if (copy_to_user(user_arg, &args, sizeof(args)))
698 return -EFAULT;
699
700 return ret;
701 }
702
703 case TEGRA_DC_EXT_GET_CURSOR:
704 return tegra_dc_ext_get_cursor(user);
705 case TEGRA_DC_EXT_PUT_CURSOR:
706 return tegra_dc_ext_put_cursor(user);
707 case TEGRA_DC_EXT_SET_CURSOR_IMAGE:
708 {
709 struct tegra_dc_ext_cursor_image args;
710
711 if (copy_from_user(&args, user_arg, sizeof(args)))
712 return -EFAULT;
713
714 return tegra_dc_ext_set_cursor_image(user, &args);
715 }
716 case TEGRA_DC_EXT_SET_CURSOR:
717 {
718 struct tegra_dc_ext_cursor args;
719
720 if (copy_from_user(&args, user_arg, sizeof(args)))
721 return -EFAULT;
722
723 return tegra_dc_ext_set_cursor(user, &args);
724 }
725
726 case TEGRA_DC_EXT_SET_CSC:
727 {
728 struct tegra_dc_ext_csc args;
729
730 if (copy_from_user(&args, user_arg, sizeof(args)))
731 return -EFAULT;
732
733 return tegra_dc_ext_set_csc(user, &args);
734 }
735
736 case TEGRA_DC_EXT_GET_VBLANK_SYNCPT:
737 {
738 u32 syncpt = tegra_dc_ext_get_vblank_syncpt(user);
739
740 if (copy_to_user(user_arg, &syncpt, sizeof(syncpt)))
741 return -EFAULT;
742
743 return 0;
744 }
745
746 case TEGRA_DC_EXT_GET_STATUS:
747 {
748 struct tegra_dc_ext_status args;
749 int ret;
750
751 ret = tegra_dc_ext_get_status(user, &args);
752
753 if (copy_to_user(user_arg, &args, sizeof(args)))
754 return -EFAULT;
755
756 return ret;
757 }
758
759 case TEGRA_DC_EXT_SET_LUT:
760 {
761 struct tegra_dc_ext_lut args;
762
763 if (copy_from_user(&args, user_arg, sizeof(args)))
764 return -EFAULT;
765
766 return tegra_dc_ext_set_lut(user, &args);
767 }
768
769 default:
770 return -EINVAL;
771 }
772}
773
774static int tegra_dc_open(struct inode *inode, struct file *filp)
775{
776 struct tegra_dc_ext_user *user;
777 struct tegra_dc_ext *ext;
778
779 user = kzalloc(sizeof(*user), GFP_KERNEL);
780 if (!user)
781 return -ENOMEM;
782
783 ext = container_of(inode->i_cdev, struct tegra_dc_ext, cdev);
784 user->ext = ext;
785
786 filp->private_data = user;
787
788 return 0;
789}
790
791static int tegra_dc_release(struct inode *inode, struct file *filp)
792{
793 struct tegra_dc_ext_user *user = filp->private_data;
794 struct tegra_dc_ext *ext = user->ext;
795 unsigned int i;
796
797 for (i = 0; i < DC_N_WINDOWS; i++) {
798 if (ext->win[i].user == user)
799 tegra_dc_ext_put_window(user, i);
800 }
801 if (ext->cursor.user == user)
802 tegra_dc_ext_put_cursor(user);
803
804 if (user->nvmap)
805 nvmap_client_put(user->nvmap);
806
807 kfree(user);
808
809 return 0;
810}
811
812static int tegra_dc_ext_setup_windows(struct tegra_dc_ext *ext)
813{
814 int i, ret;
815
816 for (i = 0; i < ext->dc->n_windows; i++) {
817 struct tegra_dc_ext_win *win = &ext->win[i];
818 char name[32];
819
820 win->ext = ext;
821 win->idx = i;
822
823 snprintf(name, sizeof(name), "tegradc.%d/%c",
824 ext->dc->ndev->id, 'a' + i);
825 win->flip_wq = create_singlethread_workqueue(name);
826 if (!win->flip_wq) {
827 ret = -ENOMEM;
828 goto cleanup;
829 }
830
831 mutex_init(&win->lock);
832 }
833
834 return 0;
835
836cleanup:
837 while (i--) {
838 struct tegra_dc_ext_win *win = &ext->win[i];
839 destroy_workqueue(win->flip_wq);
840 }
841
842 return ret;
843}
844
845static const struct file_operations tegra_dc_devops = {
846 .owner = THIS_MODULE,
847 .open = tegra_dc_open,
848 .release = tegra_dc_release,
849 .unlocked_ioctl = tegra_dc_ioctl,
850};
851
852struct tegra_dc_ext *tegra_dc_ext_register(struct nvhost_device *ndev,
853 struct tegra_dc *dc)
854{
855 int ret;
856 struct tegra_dc_ext *ext;
857 int devno;
858
859 ext = kzalloc(sizeof(*ext), GFP_KERNEL);
860 if (!ext)
861 return ERR_PTR(-ENOMEM);
862
863 BUG_ON(!tegra_dc_ext_devno);
864 devno = tegra_dc_ext_devno + head_count + 1;
865
866 cdev_init(&ext->cdev, &tegra_dc_devops);
867 ext->cdev.owner = THIS_MODULE;
868 ret = cdev_add(&ext->cdev, devno, 1);
869 if (ret) {
870 dev_err(&ndev->dev, "Failed to create character device\n");
871 goto cleanup_alloc;
872 }
873
874 ext->dev = device_create(tegra_dc_ext_class,
875 &ndev->dev,
876 devno,
877 NULL,
878 "tegra_dc_%d",
879 ndev->id);
880
881 if (IS_ERR(ext->dev)) {
882 ret = PTR_ERR(ext->dev);
883 goto cleanup_cdev;
884 }
885
886 ext->dc = dc;
887
888 ext->nvmap = nvmap_create_client(nvmap_dev, "tegra_dc_ext");
889 if (!ext->nvmap) {
890 ret = -ENOMEM;
891 goto cleanup_device;
892 }
893
894 ret = tegra_dc_ext_setup_windows(ext);
895 if (ret)
896 goto cleanup_nvmap;
897
898 mutex_init(&ext->cursor.lock);
899
900 head_count++;
901
902 return ext;
903
904cleanup_nvmap:
905 nvmap_client_put(ext->nvmap);
906
907cleanup_device:
908 device_del(ext->dev);
909
910cleanup_cdev:
911 cdev_del(&ext->cdev);
912
913cleanup_alloc:
914 kfree(ext);
915
916 return ERR_PTR(ret);
917}
918
919void tegra_dc_ext_unregister(struct tegra_dc_ext *ext)
920{
921 int i;
922
923 for (i = 0; i < ext->dc->n_windows; i++) {
924 struct tegra_dc_ext_win *win = &ext->win[i];
925
926 flush_workqueue(win->flip_wq);
927 destroy_workqueue(win->flip_wq);
928 }
929
930 nvmap_client_put(ext->nvmap);
931 device_del(ext->dev);
932 cdev_del(&ext->cdev);
933
934 kfree(ext);
935
936 head_count--;
937}
938
939int __init tegra_dc_ext_module_init(void)
940{
941 int ret;
942
943 tegra_dc_ext_class = class_create(THIS_MODULE, "tegra_dc_ext");
944 if (!tegra_dc_ext_class) {
945 printk(KERN_ERR "tegra_dc_ext: failed to create class\n");
946 return -ENOMEM;
947 }
948
949 /* Reserve one character device per head, plus the control device */
950 ret = alloc_chrdev_region(&tegra_dc_ext_devno,
951 0, TEGRA_MAX_DC + 1,
952 "tegra_dc_ext");
953 if (ret)
954 goto cleanup_class;
955
956 ret = tegra_dc_ext_control_init();
957 if (ret)
958 goto cleanup_region;
959
960 return 0;
961
962cleanup_region:
963 unregister_chrdev_region(tegra_dc_ext_devno, TEGRA_MAX_DC);
964
965cleanup_class:
966 class_destroy(tegra_dc_ext_class);
967
968 return ret;
969}
970
971void __exit tegra_dc_ext_module_exit(void)
972{
973 unregister_chrdev_region(tegra_dc_ext_devno, TEGRA_MAX_DC);
974 class_destroy(tegra_dc_ext_class);
975}
diff --git a/drivers/video/tegra/dc/ext/events.c b/drivers/video/tegra/dc/ext/events.c
new file mode 100644
index 00000000000..150a1501fce
--- /dev/null
+++ b/drivers/video/tegra/dc/ext/events.c
@@ -0,0 +1,197 @@
1/*
2 * drivers/video/tegra/dc/ext/events.c
3 *
4 * Copyright (C) 2011, NVIDIA Corporation
5 *
6 * Author: Robert Morell <rmorell@nvidia.com>
7 *
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.
12 *
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
16 * more details.
17 */
18
19#include <linux/err.h>
20#include <linux/fs.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>
26
27#include "tegra_dc_ext_priv.h"
28
29static DECLARE_WAIT_QUEUE_HEAD(event_wait);
30
31unsigned int tegra_dc_ext_event_poll(struct file *filp, poll_table *wait)
32{
33 struct tegra_dc_ext_control_user *user = filp->private_data;
34 unsigned int mask = 0;
35
36 poll_wait(filp, &event_wait, wait);
37
38 if (atomic_read(&user->num_events))
39 mask |= POLLIN;
40
41 return mask;
42}
43
44static int get_next_event(struct tegra_dc_ext_control_user *user,
45 struct tegra_dc_ext_event_list *event,
46 bool block)
47{
48 struct list_head *list = &user->event_list;
49 struct tegra_dc_ext_event_list *next_event;
50 int ret;
51
52 if (block) {
53 ret = wait_event_interruptible(event_wait,
54 atomic_read(&user->num_events));
55
56 if (unlikely(ret)) {
57 if (ret == -ERESTARTSYS)
58 return -EAGAIN;
59 return ret;
60 }
61 } else {
62 if (!atomic_read(&user->num_events))
63 return 0;
64 }
65
66 mutex_lock(&user->lock);
67
68 BUG_ON(list_empty(list));
69 next_event = list_first_entry(list, struct tegra_dc_ext_event_list,
70 list);
71 *event = *next_event;
72 list_del(&next_event->list);
73 kfree(next_event);
74
75 atomic_dec(&user->num_events);
76
77 mutex_unlock(&user->lock);
78
79 return 1;
80}
81
82ssize_t tegra_dc_ext_event_read(struct file *filp, char __user *buf,
83 size_t size, loff_t *ppos)
84{
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;
90 char *to_copy_ptr;
91
92 if (size == 0)
93 return 0;
94
95 if (user->partial_copy) {
96 /*
97 * We didn't transfer the entire event last time, need to
98 * finish it up
99 */
100 event_elem = user->event_to_copy;
101 previously_copied = user->partial_copy;
102 } else {
103 /* Get the next event, if any */
104 pending = get_next_event(user, &event_elem,
105 !(filp->f_flags & O_NONBLOCK));
106 if (pending <= 0)
107 return pending;
108 }
109
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;
114
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)) {
118 retval = -EFAULT;
119 to_copy = 0;
120 }
121
122 /* Note that we currently only deliver one event at a time */
123
124 if (event_size > to_copy) {
125 /*
126 * We were only able to copy part of this event. Stash it for
127 * next time.
128 */
129 user->event_to_copy = event_elem;
130 user->partial_copy = previously_copied + to_copy;
131 } else {
132 user->partial_copy = 0;
133 }
134
135 return to_copy ? to_copy : retval;
136}
137
138static int tegra_dc_ext_queue_event(struct tegra_dc_ext_control *control,
139 struct tegra_dc_ext_event *event)
140{
141 struct list_head *cur;
142 int retval = 0;
143
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;
148
149 user = container_of(cur, struct tegra_dc_ext_control_user,
150 list);
151 mutex_lock(&user->lock);
152
153 if (!(user->event_mask & event->type)) {
154 mutex_unlock(&user->lock);
155 continue;
156 }
157
158 ev_list = kmalloc(sizeof(*ev_list), GFP_KERNEL);
159 if (!ev_list) {
160 retval = -ENOMEM;
161 mutex_unlock(&user->lock);
162 continue;
163 }
164
165 memcpy(&ev_list->event, event,
166 sizeof(*event) + event->data_size);
167
168 list_add_tail(&ev_list->list, &user->event_list);
169
170 atomic_inc(&user->num_events);
171
172 mutex_unlock(&user->lock);
173 }
174 mutex_unlock(&control->lock);
175
176 /* Is it worth it to track waiters with more granularity? */
177 wake_up(&event_wait);
178
179 return retval;
180}
181
182int tegra_dc_ext_queue_hotplug(struct tegra_dc_ext_control *control, int output)
183{
184 struct {
185 struct tegra_dc_ext_event event;
186 struct tegra_dc_ext_control_event_hotplug hotplug;
187 } __packed pack;
188
189 pack.event.type = TEGRA_DC_EXT_EVENT_HOTPLUG;
190 pack.event.data_size = sizeof(pack.hotplug);
191
192 pack.hotplug.handle = output;
193
194 tegra_dc_ext_queue_event(control, &pack.event);
195
196 return 0;
197}
diff --git a/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h b/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h
new file mode 100644
index 00000000000..95a637d5a52
--- /dev/null
+++ b/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h
@@ -0,0 +1,147 @@
1/*
2 * drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h
3 *
4 * Copyright (C) 2011, NVIDIA Corporation
5 *
6 * Author: Robert Morell <rmorell@nvidia.com>
7 *
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.
12 *
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
16 * more details.
17 */
18
19#ifndef __TEGRA_DC_EXT_PRIV_H
20#define __TEGRA_DC_EXT_PRIV_H
21
22#include <linux/cdev.h>
23#include <linux/list.h>
24#include <linux/mutex.h>
25#include <linux/poll.h>
26
27#include <mach/dc.h>
28#include <mach/nvmap.h>
29
30#include <video/tegra_dc_ext.h>
31
32struct tegra_dc_ext;
33
34struct tegra_dc_ext_user {
35 struct tegra_dc_ext *ext;
36 struct nvmap_client *nvmap;
37};
38
39enum {
40 TEGRA_DC_Y,
41 TEGRA_DC_U,
42 TEGRA_DC_V,
43 TEGRA_DC_NUM_PLANES,
44};
45
46struct tegra_dc_ext_win {
47 struct tegra_dc_ext *ext;
48
49 int idx;
50
51 struct tegra_dc_ext_user *user;
52
53 struct mutex lock;
54
55 /* Current nvmap handle (if any) for Y, U, V planes */
56 struct nvmap_handle_ref *cur_handle[TEGRA_DC_NUM_PLANES];
57
58 struct workqueue_struct *flip_wq;
59
60 atomic_t nr_pending_flips;
61};
62
63struct tegra_dc_ext {
64 struct tegra_dc *dc;
65
66 struct cdev cdev;
67 struct device *dev;
68
69 struct nvmap_client *nvmap;
70
71 struct tegra_dc_ext_win win[DC_N_WINDOWS];
72
73 struct {
74 struct tegra_dc_ext_user *user;
75 struct nvmap_handle_ref *cur_handle;
76 struct mutex lock;
77 } cursor;
78
79 bool enabled;
80};
81
82#define TEGRA_DC_EXT_EVENT_MASK_ALL \
83 TEGRA_DC_EXT_EVENT_HOTPLUG
84
85#define TEGRA_DC_EXT_EVENT_MAX_SZ 8
86
87struct tegra_dc_ext_event_list {
88 struct tegra_dc_ext_event event;
89 /* The data field _must_ follow the event field. */
90 char data[TEGRA_DC_EXT_EVENT_MAX_SZ];
91
92 struct list_head list;
93};
94
95#define TEGRA_DC_EXT_CAPABILITIES \
96 TEGRA_DC_EXT_CAPABILITIES_CURSOR_MODE
97
98struct tegra_dc_ext_control_user {
99 struct tegra_dc_ext_control *control;
100
101 struct list_head event_list;
102 atomic_t num_events;
103
104 u32 event_mask;
105
106 struct tegra_dc_ext_event_list event_to_copy;
107 loff_t partial_copy;
108
109 struct mutex lock;
110
111 struct list_head list;
112};
113
114struct tegra_dc_ext_control {
115 struct cdev cdev;
116 struct device *dev;
117
118 struct list_head users;
119
120 struct mutex lock;
121};
122
123extern int tegra_dc_ext_devno;
124extern struct class *tegra_dc_ext_class;
125
126extern int tegra_dc_ext_pin_window(struct tegra_dc_ext_user *user, u32 id,
127 struct nvmap_handle_ref **handle,
128 dma_addr_t *phys_addr);
129
130extern int tegra_dc_ext_get_cursor(struct tegra_dc_ext_user *user);
131extern int tegra_dc_ext_put_cursor(struct tegra_dc_ext_user *user);
132extern int tegra_dc_ext_set_cursor_image(struct tegra_dc_ext_user *user,
133 struct tegra_dc_ext_cursor_image *);
134extern int tegra_dc_ext_set_cursor(struct tegra_dc_ext_user *user,
135 struct tegra_dc_ext_cursor *);
136
137extern int tegra_dc_ext_control_init(void);
138
139extern int tegra_dc_ext_queue_hotplug(struct tegra_dc_ext_control *,
140 int output);
141extern ssize_t tegra_dc_ext_event_read(struct file *filp, char __user *buf,
142 size_t size, loff_t *ppos);
143extern unsigned int tegra_dc_ext_event_poll(struct file *, poll_table *);
144
145extern int tegra_dc_ext_get_num_outputs(void);
146
147#endif /* __TEGRA_DC_EXT_PRIV_H */
diff --git a/drivers/video/tegra/dc/ext/util.c b/drivers/video/tegra/dc/ext/util.c
new file mode 100644
index 00000000000..747085579f1
--- /dev/null
+++ b/drivers/video/tegra/dc/ext/util.c
@@ -0,0 +1,78 @@
1/*
2 * drivers/video/tegra/dc/ext/util.c
3 *
4 * Copyright (C) 2011, NVIDIA Corporation
5 *
6 * Author: Robert Morell <rmorell@nvidia.com>
7 *
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.
12 *
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
16 * more details.
17 */
18
19#include <linux/err.h>
20#include <linux/types.h>
21
22#include <mach/dc.h>
23#include <mach/nvmap.h>
24
25/* ugh */
26#include "../../nvmap/nvmap.h"
27
28#include "tegra_dc_ext_priv.h"
29
30int tegra_dc_ext_pin_window(struct tegra_dc_ext_user *user, u32 id,
31 struct nvmap_handle_ref **handle,
32 dma_addr_t *phys_addr)
33{
34 struct tegra_dc_ext *ext = user->ext;
35 struct nvmap_handle_ref *win_dup;
36 struct nvmap_handle *win_handle;
37 dma_addr_t phys;
38
39 if (!id) {
40 *handle = NULL;
41 *phys_addr = -1;
42
43 return 0;
44 }
45
46 /*
47 * Take a reference to the buffer using the user's nvmap context, to
48 * make sure they have permissions to access it.
49 */
50 win_handle = nvmap_get_handle_id(user->nvmap, id);
51 if (!win_handle)
52 return -EACCES;
53
54 /*
55 * Duplicate the buffer's handle into the dc_ext driver's nvmap
56 * context, to ensure that the handle won't be freed as long as it is
57 * in use by display.
58 */
59 win_dup = nvmap_duplicate_handle_id(ext->nvmap, id);
60
61 /* Release the reference we took in the user's context above */
62 nvmap_handle_put(win_handle);
63
64 if (IS_ERR(win_dup))
65 return PTR_ERR(win_dup);
66
67 phys = nvmap_pin(ext->nvmap, win_dup);
68 /* XXX this isn't correct for non-pointers... */
69 if (IS_ERR((void *)phys)) {
70 nvmap_free(ext->nvmap, win_dup);
71 return PTR_ERR((void *)phys);
72 }
73
74 *phys_addr = phys;
75 *handle = win_dup;
76
77 return 0;
78}