ecfa08c31e94a5955a08effdb5f1628d37837b55
[linux-2.6.git] / drivers / video / tegra / host / host1x / host1x_syncpt.c
1 /*
2  * drivers/video/tegra/host/host1x/host1x_syncpt.c
3  *
4  * Tegra Graphics Host Syncpoints for HOST1X
5  *
6  * Copyright (c) 2010-2011, NVIDIA Corporation.
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  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21  */
22
23 #include <linux/nvhost_ioctl.h>
24 #include "nvhost_syncpt.h"
25 #include "dev.h"
26 #include "host1x_syncpt.h"
27 #include "host1x_hardware.h"
28
29 /**
30  * Write the current syncpoint value back to hw.
31  */
32 static void t20_syncpt_reset(struct nvhost_syncpt *sp, u32 id)
33 {
34         struct nvhost_master *dev = syncpt_to_dev(sp);
35         int min = nvhost_syncpt_read_min(sp, id);
36         writel(min, dev->sync_aperture + (HOST1X_SYNC_SYNCPT_0 + id * 4));
37 }
38
39 /**
40  * Write the current waitbase value back to hw.
41  */
42 static void t20_syncpt_reset_wait_base(struct nvhost_syncpt *sp, u32 id)
43 {
44         struct nvhost_master *dev = syncpt_to_dev(sp);
45         writel(sp->base_val[id],
46                 dev->sync_aperture + (HOST1X_SYNC_SYNCPT_BASE_0 + id * 4));
47 }
48
49 /**
50  * Read waitbase value from hw.
51  */
52 static void t20_syncpt_read_wait_base(struct nvhost_syncpt *sp, u32 id)
53 {
54         struct nvhost_master *dev = syncpt_to_dev(sp);
55         sp->base_val[id] = readl(dev->sync_aperture +
56                                 (HOST1X_SYNC_SYNCPT_BASE_0 + id * 4));
57 }
58
59 /**
60  * Updates the last value read from hardware.
61  * (was nvhost_syncpt_update_min)
62  */
63 static u32 t20_syncpt_update_min(struct nvhost_syncpt *sp, u32 id)
64 {
65         struct nvhost_master *dev = syncpt_to_dev(sp);
66         void __iomem *sync_regs = dev->sync_aperture;
67         u32 old, live;
68
69         do {
70                 old = nvhost_syncpt_read_min(sp, id);
71                 live = readl(sync_regs + (HOST1X_SYNC_SYNCPT_0 + id * 4));
72         } while ((u32)atomic_cmpxchg(&sp->min_val[id], old, live) != old);
73
74         if (!nvhost_syncpt_check_max(sp, id, live)) {
75                 dev_err(&syncpt_to_dev(sp)->pdev->dev,
76                                 "%s failed: id=%u\n",
77                                 __func__,
78                                 id);
79                 nvhost_debug_dump(syncpt_to_dev(sp));
80                 BUG();
81         }
82         return live;
83 }
84
85 /**
86  * Write a cpu syncpoint increment to the hardware, without touching
87  * the cache. Caller is responsible for host being powered.
88  */
89 static void t20_syncpt_cpu_incr(struct nvhost_syncpt *sp, u32 id)
90 {
91         struct nvhost_master *dev = syncpt_to_dev(sp);
92         BUG_ON(!nvhost_module_powered(&dev->mod));
93         if (!client_managed(id) && nvhost_syncpt_min_eq_max(sp, id)) {
94                 dev_err(&syncpt_to_dev(sp)->pdev->dev,
95                                 "Syncpoint id %d\n",
96                                 id);
97                 nvhost_debug_dump(syncpt_to_dev(sp));
98                 BUG();
99         }
100         writel(BIT(id), dev->sync_aperture + HOST1X_SYNC_SYNCPT_CPU_INCR);
101         wmb();
102 }
103
104 /* returns true, if a <= b < c using wrapping comparison */
105 static inline bool nvhost_syncpt_is_between(u32 a, u32 b, u32 c)
106 {
107         return b-a < c-a;
108 }
109
110 /* returns true, if syncpt >= threshold (mod 1 << 32) */
111 static bool nvhost_syncpt_wrapping_comparison(u32 syncpt, u32 threshold)
112 {
113         return nvhost_syncpt_is_between(threshold, syncpt,
114                                         (1UL<<31UL)+threshold);
115 }
116
117 /* check for old WAITs to be removed (avoiding a wrap) */
118 static int t20_syncpt_wait_check(struct nvhost_syncpt *sp,
119                                  struct nvmap_client *nvmap,
120                                  u32 waitchk_mask,
121                                  struct nvhost_waitchk *wait,
122                                  int num_waitchk)
123 {
124         u32 idx;
125         int err = 0;
126
127         /* get current syncpt values */
128         for (idx = 0; idx < NV_HOST1X_SYNCPT_NB_PTS; idx++) {
129                 if (BIT(idx) & waitchk_mask)
130                         nvhost_syncpt_update_min(sp, idx);
131         }
132
133         BUG_ON(!wait && !num_waitchk);
134
135         /* compare syncpt vs wait threshold */
136         while (num_waitchk) {
137                 u32 syncpt, override;
138
139                 BUG_ON(wait->syncpt_id >= NV_HOST1X_SYNCPT_NB_PTS);
140
141                 syncpt = atomic_read(&sp->min_val[wait->syncpt_id]);
142                 if (nvhost_syncpt_wrapping_comparison(syncpt, wait->thresh)) {
143                         /*
144                          * NULL an already satisfied WAIT_SYNCPT host method,
145                          * by patching its args in the command stream. The
146                          * method data is changed to reference a reserved
147                          * (never given out or incr) NVSYNCPT_GRAPHICS_HOST
148                          * syncpt with a matching threshold value of 0, so
149                          * is guaranteed to be popped by the host HW.
150                          */
151                         dev_dbg(&syncpt_to_dev(sp)->pdev->dev,
152                             "drop WAIT id %d (%s) thresh 0x%x, syncpt 0x%x\n",
153                             wait->syncpt_id,
154                             syncpt_op(sp).name(sp, wait->syncpt_id),
155                             wait->thresh, syncpt);
156
157                         /* patch the wait */
158                         override = nvhost_class_host_wait_syncpt(
159                                         NVSYNCPT_GRAPHICS_HOST, 0);
160                         err = nvmap_patch_word(nvmap,
161                                         (struct nvmap_handle *)wait->mem,
162                                         wait->offset, override);
163                         if (err)
164                                 break;
165                 }
166
167                 wait++;
168                 num_waitchk--;
169         }
170         return err;
171 }
172
173
174 static const char *s_syncpt_names[32] = {
175         "gfx_host",
176         "", "", "", "", "", "", "",
177         "disp0_a", "disp1_a", "avp_0",
178         "csi_vi_0", "csi_vi_1",
179         "vi_isp_0", "vi_isp_1", "vi_isp_2", "vi_isp_3", "vi_isp_4",
180         "2d_0", "2d_1",
181         "disp0_b", "disp1_b",
182         "3d",
183         "mpe",
184         "disp0_c", "disp1_c",
185         "vblank0", "vblank1",
186         "mpe_ebm_eof", "mpe_wr_safe",
187         "2d_tinyblt",
188         "dsi"
189 };
190
191 static const char *t20_syncpt_name(struct nvhost_syncpt *s, u32 id)
192 {
193         BUG_ON(id >= ARRAY_SIZE(s_syncpt_names));
194         return s_syncpt_names[id];
195 }
196
197 static void t20_syncpt_debug(struct nvhost_syncpt *sp)
198 {
199         u32 i;
200         for (i = 0; i < NV_HOST1X_SYNCPT_NB_PTS; i++) {
201                 u32 max = nvhost_syncpt_read_max(sp, i);
202                 if (!max)
203                         continue;
204                 dev_info(&syncpt_to_dev(sp)->pdev->dev,
205                         "id %d (%s) min %d max %d\n",
206                          i, syncpt_op(sp).name(sp, i),
207                         nvhost_syncpt_update_min(sp, i), max);
208
209         }
210
211         for (i = 0; i < NV_HOST1X_SYNCPT_NB_BASES; i++) {
212                 u32 base_val;
213                 t20_syncpt_read_wait_base(sp, i);
214                 base_val = sp->base_val[i];
215                 if (base_val)
216                         dev_info(&syncpt_to_dev(sp)->pdev->dev,
217                                         "waitbase id %d val %d\n",
218                                         i, base_val);
219
220         }
221 }
222
223 int host1x_init_syncpt_support(struct nvhost_master *host)
224 {
225
226         host->sync_aperture = host->aperture +
227                 (NV_HOST1X_CHANNEL0_BASE +
228                         HOST1X_CHANNEL_SYNC_REG_BASE);
229
230         host->op.syncpt.reset = t20_syncpt_reset;
231         host->op.syncpt.reset_wait_base = t20_syncpt_reset_wait_base;
232         host->op.syncpt.read_wait_base = t20_syncpt_read_wait_base;
233         host->op.syncpt.update_min = t20_syncpt_update_min;
234         host->op.syncpt.cpu_incr = t20_syncpt_cpu_incr;
235         host->op.syncpt.wait_check = t20_syncpt_wait_check;
236         host->op.syncpt.debug = t20_syncpt_debug;
237         host->op.syncpt.name = t20_syncpt_name;
238
239         host->syncpt.nb_pts = NV_HOST1X_SYNCPT_NB_PTS;
240         host->syncpt.nb_bases = NV_HOST1X_SYNCPT_NB_BASES;
241         host->syncpt.client_managed = NVSYNCPTS_CLIENT_MANAGED;
242
243         return 0;
244 }