video: tegra: dc: Optimize OS idle display client wakeup
[linux-3.10.git] / drivers / video / tegra / dc / ext / cursor.c
1 /*
2  * drivers/video/tegra/dc/ext/cursor.c
3  *
4  * Copyright (c) 2011-2012, NVIDIA CORPORATION, All rights reserved.
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
27 int 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
44 int 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
61 static 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         unsigned long val;
66         int clip_win;
67
68         tegra_dc_writel(dc,
69                 CURSOR_COLOR(args->foreground.r,
70                              args->foreground.g,
71                              args->foreground.b),
72                 DC_DISP_CURSOR_FOREGROUND);
73         tegra_dc_writel(dc,
74                 CURSOR_COLOR(args->background.r,
75                              args->background.g,
76                              args->background.b),
77                 DC_DISP_CURSOR_BACKGROUND);
78
79         BUG_ON(phys_addr & ~CURSOR_START_ADDR_MASK);
80
81         switch (TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE(args->flags)) {
82         case TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_64x64:
83                 val = CURSOR_SIZE_64;
84                 break;
85         case TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_128x128:
86                 val = CURSOR_SIZE_128;
87                 break;
88         case TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_256x256:
89                 val = CURSOR_SIZE_256;
90                 break;
91         default:
92                 val = 0;
93         }
94
95         /* Get the cursor clip window number */
96         clip_win = CURSOR_CLIP_GET_WINDOW(tegra_dc_readl(dc,
97                                           DC_DISP_CURSOR_START_ADDR));
98         val |= clip_win;
99
100         tegra_dc_writel(dc,
101                 val | CURSOR_START_ADDR(((unsigned long) phys_addr)),
102                 DC_DISP_CURSOR_START_ADDR);
103
104         if (args->flags & TEGRA_DC_EXT_CURSOR_FLAGS_RGBA_NORMAL)
105                 tegra_dc_writel(dc,
106                                 CURSOR_MODE_SELECT(1),
107                                 DC_DISP_BLEND_CURSOR_CONTROL);
108         else
109                 tegra_dc_writel(dc,
110                                 CURSOR_MODE_SELECT(0),
111                                 DC_DISP_BLEND_CURSOR_CONTROL);
112 }
113
114 int tegra_dc_ext_set_cursor_image(struct tegra_dc_ext_user *user,
115                                   struct tegra_dc_ext_cursor_image *args)
116 {
117         struct tegra_dc_ext *ext = user->ext;
118         struct tegra_dc *dc = ext->dc;
119         struct nvmap_handle_ref *handle, *old_handle;
120         dma_addr_t phys_addr;
121         u32 size;
122         int ret;
123
124         if (!user->nvmap)
125                 return -EFAULT;
126
127         size = TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE(args->flags);
128 #if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
129         if (size != TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_32x32 &&
130             size !=  TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_64x64)
131                 return -EINVAL;
132 #else
133         if (size != TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_32x32 &&
134             size !=  TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_64x64 &&
135             size !=  TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_128x128 &&
136             size !=  TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_256x256)
137                 return -EINVAL;
138 #endif
139
140 #if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
141         if (args->flags && TEGRA_DC_EXT_CURSOR_FLAGS_RGBA_NORMAL)
142                 return -EINVAL;
143 #endif
144
145         mutex_lock(&ext->cursor.lock);
146
147         if (ext->cursor.user != user) {
148                 ret = -EACCES;
149                 goto unlock;
150         }
151
152         if (!ext->enabled) {
153                 ret = -ENXIO;
154                 goto unlock;
155         }
156
157         old_handle = ext->cursor.cur_handle;
158
159         ret = tegra_dc_ext_pin_window(user, args->buff_id, &handle, &phys_addr);
160         if (ret)
161                 goto unlock;
162
163         ext->cursor.cur_handle = handle;
164
165         mutex_lock(&dc->lock);
166         tegra_dc_get(dc);
167
168         set_cursor_image_hw(dc, args, phys_addr);
169
170         tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
171         tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
172
173         tegra_dc_put(dc);
174         /* XXX sync here? */
175
176         mutex_unlock(&dc->lock);
177
178         mutex_unlock(&ext->cursor.lock);
179
180         if (old_handle) {
181                 nvmap_unpin(ext->nvmap, old_handle);
182                 nvmap_free(ext->nvmap, old_handle);
183         }
184
185         return 0;
186
187 unlock:
188         mutex_unlock(&ext->cursor.lock);
189
190         return ret;
191 }
192
193 int tegra_dc_ext_set_cursor(struct tegra_dc_ext_user *user,
194                             struct tegra_dc_ext_cursor *args)
195 {
196         struct tegra_dc_ext *ext = user->ext;
197         struct tegra_dc *dc = ext->dc;
198         u32 val;
199         bool enable;
200         int ret;
201
202         mutex_lock(&ext->cursor.lock);
203
204         if (ext->cursor.user != user) {
205                 ret = -EACCES;
206                 goto unlock;
207         }
208
209         if (!ext->enabled) {
210                 ret = -ENXIO;
211                 goto unlock;
212         }
213
214         enable = !!(args->flags & TEGRA_DC_EXT_CURSOR_FLAGS_VISIBLE);
215
216         mutex_lock(&dc->lock);
217         tegra_dc_get(dc);
218
219         val = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL);
220         val &= ~WINH_CURS_SELECT(1);
221         tegra_dc_writel(dc, val, DC_DISP_BLEND_CURSOR_CONTROL);
222
223         val = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
224         if (!!(val & CURSOR_ENABLE) != enable) {
225                 val &= ~CURSOR_ENABLE;
226                 if (enable)
227                         val |= CURSOR_ENABLE;
228                 tegra_dc_writel(dc, val, DC_DISP_DISP_WIN_OPTIONS);
229         }
230
231         tegra_dc_writel(dc, CURSOR_POSITION(args->x, args->y),
232                 DC_DISP_CURSOR_POSITION);
233
234         tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
235         tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
236
237         /* TODO: need to sync here?  hopefully can avoid this, but need to
238          * figure out interaction w/ rest of GENERAL_ACT_REQ */
239
240         tegra_dc_put(dc);
241         mutex_unlock(&dc->lock);
242
243         mutex_unlock(&ext->cursor.lock);
244
245         return 0;
246
247 unlock:
248         mutex_unlock(&ext->cursor.lock);
249
250         return ret;
251 }
252
253 int tegra_dc_ext_cursor_clip(struct tegra_dc_ext_user *user,
254                             int *args)
255 {
256         struct tegra_dc_ext *ext = user->ext;
257         struct tegra_dc *dc = ext->dc;
258         int ret;
259         unsigned long reg_val;
260
261         mutex_lock(&ext->cursor.lock);
262
263         if (ext->cursor.user != user) {
264                 ret = -EACCES;
265                 goto unlock;
266         }
267
268         if (!ext->enabled) {
269                 ret = -ENXIO;
270                 goto unlock;
271         }
272
273         mutex_lock(&dc->lock);
274         tegra_dc_get(dc);
275
276         reg_val = tegra_dc_readl(dc, DC_DISP_CURSOR_START_ADDR);
277         reg_val &= ~CURSOR_CLIP_SHIFT_BITS(3); /* Clear out the old value */
278         tegra_dc_writel(dc, reg_val | CURSOR_CLIP_SHIFT_BITS(*args),
279                         DC_DISP_CURSOR_START_ADDR);
280
281         tegra_dc_put(dc);
282         mutex_unlock(&dc->lock);
283
284         mutex_unlock(&ext->cursor.lock);
285
286         return 0;
287
288 unlock:
289         mutex_unlock(&ext->cursor.lock);
290
291         return ret;
292 }