6abfeb833b5532c9fc28a666300e6054940f85b6
[linux-2.6.git] / drivers / video / tegra / host / msenc / msenc.c
1 /*
2  * drivers/video/tegra/host/msenc/msenc.c
3  *
4  * Tegra MSENC Module Support
5  *
6  * Copyright (c) 2012-2013, NVIDIA CORPORATION.  All rights reserved.
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms and conditions of the GNU General Public License,
10  * version 2, as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15  * more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <linux/slab.h>         /* for kzalloc */
22 #include <linux/firmware.h>
23 #include <linux/module.h>
24 #include <linux/pm_runtime.h>
25 #include <mach/clk.h>
26 #include <asm/byteorder.h>      /* for parsing ucode image wrt endianness */
27 #include <linux/delay.h>        /* for udelay */
28 #include <linux/scatterlist.h>
29 #include <mach/iomap.h>
30 #include "dev.h"
31 #include "msenc.h"
32 #include "hw_msenc.h"
33 #include "bus_client.h"
34 #include "nvhost_acm.h"
35 #include "chip_support.h"
36 #include "nvhost_memmgr.h"
37
38 #define MSENC_IDLE_TIMEOUT_DEFAULT      10000   /* 10 milliseconds */
39 #define MSENC_IDLE_CHECK_PERIOD         10      /* 10 usec */
40
41 #define get_msenc(ndev) ((struct msenc *)(ndev)->dev.platform_data)
42 #define set_msenc(ndev, f) ((ndev)->dev.platform_data = f)
43
44 /* caller is responsible for freeing */
45 static char *msenc_get_fw_name(struct platform_device *dev)
46 {
47         char *fw_name;
48         u8 maj, min;
49         struct nvhost_device_data *pdata = platform_get_drvdata(dev);
50
51         /* note size here is a little over...*/
52         fw_name = kzalloc(32, GFP_KERNEL);
53         if (!fw_name)
54                 return NULL;
55
56         decode_msenc_ver(pdata->version, &maj, &min);
57         if (maj == 2) {
58                 /* there are no minor versions so far for maj==2 */
59                 sprintf(fw_name, "nvhost_msenc02.fw");
60         } else {
61                 kfree(fw_name);
62                 return NULL;
63         }
64
65         dev_info(&dev->dev, "fw name:%s\n", fw_name);
66
67         return fw_name;
68 }
69
70 static int msenc_dma_wait_idle(struct platform_device *dev, u32 *timeout)
71 {
72         if (!*timeout)
73                 *timeout = MSENC_IDLE_TIMEOUT_DEFAULT;
74
75         do {
76                 u32 check = min_t(u32, MSENC_IDLE_CHECK_PERIOD, *timeout);
77                 u32 dmatrfcmd = nvhost_device_readl(dev, msenc_dmatrfcmd_r());
78                 u32 idle_v = msenc_dmatrfcmd_idle_v(dmatrfcmd);
79
80                 if (msenc_dmatrfcmd_idle_true_v() == idle_v)
81                         return 0;
82
83                 udelay(MSENC_IDLE_CHECK_PERIOD);
84                 *timeout -= check;
85         } while (*timeout);
86
87         dev_err(&dev->dev, "dma idle timeout");
88
89         return -1;
90 }
91
92 static int msenc_dma_pa_to_internal_256b(struct platform_device *dev,
93                 u32 offset, u32 internal_offset, bool imem)
94 {
95         u32 cmd = msenc_dmatrfcmd_size_256b_f();
96         u32 pa_offset =  msenc_dmatrffboffs_offs_f(offset);
97         u32 i_offset = msenc_dmatrfmoffs_offs_f(internal_offset);
98         u32 timeout = 0; /* default*/
99
100         if (imem)
101                 cmd |= msenc_dmatrfcmd_imem_true_f();
102
103         nvhost_device_writel(dev, msenc_dmatrfmoffs_r(), i_offset);
104         nvhost_device_writel(dev, msenc_dmatrffboffs_r(), pa_offset);
105         nvhost_device_writel(dev, msenc_dmatrfcmd_r(), cmd);
106
107         return msenc_dma_wait_idle(dev, &timeout);
108
109 }
110
111 static int msenc_wait_idle(struct platform_device *dev, u32 *timeout)
112 {
113         if (!*timeout)
114                 *timeout = MSENC_IDLE_TIMEOUT_DEFAULT;
115
116         do {
117                 u32 check = min_t(u32, MSENC_IDLE_CHECK_PERIOD, *timeout);
118                 u32 w = nvhost_device_readl(dev, msenc_idlestate_r());
119
120                 if (!w)
121                         return 0;
122                 udelay(MSENC_IDLE_CHECK_PERIOD);
123                 *timeout -= check;
124         } while (*timeout);
125
126         return -1;
127 }
128
129 int msenc_boot(struct platform_device *dev)
130 {
131         u32 timeout;
132         u32 offset;
133         int err = 0;
134         struct msenc *m = get_msenc(dev);
135
136         nvhost_device_writel(dev, msenc_dmactl_r(), 0);
137         nvhost_device_writel(dev, msenc_dmatrfbase_r(),
138                 (sg_dma_address(m->pa->sgl) + m->os.bin_data_offset) >> 8);
139
140         for (offset = 0; offset < m->os.data_size; offset += 256)
141                 msenc_dma_pa_to_internal_256b(dev,
142                                            m->os.data_offset + offset,
143                                            offset, false);
144
145         msenc_dma_pa_to_internal_256b(dev, m->os.code_offset, 0, true);
146
147         /* setup msenc interrupts and enable interface */
148         nvhost_device_writel(dev, msenc_irqmset_r(),
149                         (msenc_irqmset_ext_f(0xff) |
150                                 msenc_irqmset_swgen1_set_f() |
151                                 msenc_irqmset_swgen0_set_f() |
152                                 msenc_irqmset_exterr_set_f() |
153                                 msenc_irqmset_halt_set_f()   |
154                                 msenc_irqmset_wdtmr_set_f()));
155         nvhost_device_writel(dev, msenc_irqdest_r(),
156                         (msenc_irqdest_host_ext_f(0xff) |
157                                 msenc_irqdest_host_swgen1_host_f() |
158                                 msenc_irqdest_host_swgen0_host_f() |
159                                 msenc_irqdest_host_exterr_host_f() |
160                                 msenc_irqdest_host_halt_host_f()));
161         nvhost_device_writel(dev, msenc_itfen_r(),
162                         (msenc_itfen_mthden_enable_f() |
163                                 msenc_itfen_ctxen_enable_f()));
164
165         /* boot msenc */
166         nvhost_device_writel(dev, msenc_bootvec_r(), msenc_bootvec_vec_f(0));
167         nvhost_device_writel(dev, msenc_cpuctl_r(),
168                         msenc_cpuctl_startcpu_true_f());
169
170         timeout = 0; /* default */
171
172         err = msenc_wait_idle(dev, &timeout);
173         if (err != 0) {
174                 dev_err(&dev->dev, "boot failed due to timeout");
175                 return err;
176         }
177
178         return 0;
179 }
180
181 static int msenc_setup_ucode_image(struct platform_device *dev,
182                 u32 *ucode_ptr,
183                 const struct firmware *ucode_fw)
184 {
185         struct msenc *m = get_msenc(dev);
186         /* image data is little endian. */
187         struct msenc_ucode_v1 ucode;
188         int w;
189
190         /* copy the whole thing taking into account endianness */
191         for (w = 0; w < ucode_fw->size / sizeof(u32); w++)
192                 ucode_ptr[w] = le32_to_cpu(((u32 *)ucode_fw->data)[w]);
193
194         ucode.bin_header = (struct msenc_ucode_bin_header_v1 *)ucode_ptr;
195         /* endian problems would show up right here */
196         if (ucode.bin_header->bin_magic != 0x10de) {
197                 dev_err(&dev->dev,
198                            "failed to get firmware magic");
199                 return -EINVAL;
200         }
201         if (ucode.bin_header->bin_ver != 1) {
202                 dev_err(&dev->dev,
203                            "unsupported firmware version");
204                 return -ENOENT;
205         }
206         /* shouldn't be bigger than what firmware thinks */
207         if (ucode.bin_header->bin_size > ucode_fw->size) {
208                 dev_err(&dev->dev,
209                            "ucode image size inconsistency");
210                 return -EINVAL;
211         }
212
213         dev_dbg(&dev->dev,
214                 "ucode bin header: magic:0x%x ver:%d size:%d",
215                 ucode.bin_header->bin_magic,
216                 ucode.bin_header->bin_ver,
217                 ucode.bin_header->bin_size);
218         dev_dbg(&dev->dev,
219                 "ucode bin header: os bin (header,data) offset size: 0x%x, 0x%x %d",
220                 ucode.bin_header->os_bin_header_offset,
221                 ucode.bin_header->os_bin_data_offset,
222                 ucode.bin_header->os_bin_size);
223         ucode.os_header = (struct msenc_ucode_os_header_v1 *)
224                 (((void *)ucode_ptr) + ucode.bin_header->os_bin_header_offset);
225
226         dev_dbg(&dev->dev,
227                 "os ucode header: os code (offset,size): 0x%x, 0x%x",
228                 ucode.os_header->os_code_offset,
229                 ucode.os_header->os_code_size);
230         dev_dbg(&dev->dev,
231                 "os ucode header: os data (offset,size): 0x%x, 0x%x",
232                 ucode.os_header->os_data_offset,
233                 ucode.os_header->os_data_size);
234         dev_dbg(&dev->dev,
235                 "os ucode header: num apps: %d",
236                 ucode.os_header->num_apps);
237
238         m->os.size = ucode.bin_header->os_bin_size;
239         m->os.bin_data_offset = ucode.bin_header->os_bin_data_offset;
240         m->os.code_offset = ucode.os_header->os_code_offset;
241         m->os.data_offset = ucode.os_header->os_data_offset;
242         m->os.data_size   = ucode.os_header->os_data_size;
243
244         return 0;
245 }
246
247 int msenc_read_ucode(struct platform_device *dev, const char *fw_name)
248 {
249         struct msenc *m = get_msenc(dev);
250         const struct firmware *ucode_fw;
251         int err;
252
253         ucode_fw  = nvhost_client_request_firmware(dev, fw_name);
254         if (IS_ERR_OR_NULL(ucode_fw)) {
255                 dev_err(&dev->dev, "failed to get msenc firmware\n");
256                 err = -ENOENT;
257                 return err;
258         }
259
260         /* allocate pages for ucode */
261         m->mem_r = mem_op().alloc(nvhost_get_host(dev)->memmgr,
262                                      roundup(ucode_fw->size, PAGE_SIZE),
263                                      PAGE_SIZE, mem_mgr_flag_uncacheable);
264         if (IS_ERR_OR_NULL(m->mem_r)) {
265                 dev_err(&dev->dev, "nvmap alloc failed");
266                 err = -ENOMEM;
267                 goto clean_up;
268         }
269
270         m->pa = mem_op().pin(nvhost_get_host(dev)->memmgr, m->mem_r);
271         if (IS_ERR_OR_NULL(m->pa)) {
272                 dev_err(&dev->dev, "nvmap pin failed for ucode");
273                 err = PTR_ERR(m->pa);
274                 m->pa = NULL;
275                 goto clean_up;
276         }
277
278         m->mapped = mem_op().mmap(m->mem_r);
279         if (IS_ERR_OR_NULL(m->mapped)) {
280                 dev_err(&dev->dev, "nvmap mmap failed");
281                 err = -ENOMEM;
282                 goto clean_up;
283         }
284
285         err = msenc_setup_ucode_image(dev, (u32 *)m->mapped, ucode_fw);
286         if (err) {
287                 dev_err(&dev->dev, "failed to parse firmware image\n");
288                 return err;
289         }
290
291         m->valid = true;
292
293         release_firmware(ucode_fw);
294
295         return 0;
296
297 clean_up:
298         if (m->mapped) {
299                 mem_op().munmap(m->mem_r, (u32 *)m->mapped);
300                 m->mapped = NULL;
301         }
302         if (m->pa) {
303                 mem_op().unpin(nvhost_get_host(dev)->memmgr, m->mem_r, m->pa);
304                 m->pa = NULL;
305         }
306         if (m->mem_r) {
307                 mem_op().put(nvhost_get_host(dev)->memmgr, m->mem_r);
308                 m->mem_r = NULL;
309         }
310         release_firmware(ucode_fw);
311         return err;
312 }
313
314 void nvhost_msenc_init(struct platform_device *dev)
315 {
316         struct nvhost_device_data *pdata =
317                 (struct nvhost_device_data *)dev->dev.platform_data;
318         int err = 0;
319         struct msenc *m;
320         char *fw_name;
321
322         fw_name = msenc_get_fw_name(dev);
323         if (!fw_name) {
324                 dev_err(&dev->dev, "couldn't determine firmware name");
325                 return;
326         }
327
328         m = kzalloc(sizeof(struct msenc), GFP_KERNEL);
329         if (!m) {
330                 dev_err(&dev->dev, "couldn't alloc ucode");
331                 kfree(fw_name);
332                 return;
333         }
334         set_msenc(dev, m);
335
336         err = msenc_read_ucode(dev, fw_name);
337         kfree(fw_name);
338         fw_name = 0;
339
340         if (err || !m->valid) {
341                 dev_err(&dev->dev, "ucode not valid");
342                 goto clean_up;
343         }
344
345         if (!pdata->can_powergate) {
346                 nvhost_module_busy(dev);
347                 msenc_boot(dev);
348                 nvhost_module_idle(dev);
349         }
350
351         return;
352
353 clean_up:
354         dev_err(&dev->dev, "failed");
355 }
356
357 void nvhost_msenc_deinit(struct platform_device *dev)
358 {
359         struct msenc *m = get_msenc(dev);
360
361         /* unpin, free ucode memory */
362         if (m->mapped) {
363                 mem_op().munmap(m->mem_r, m->mapped);
364                 m->mapped = NULL;
365         }
366         if (m->pa) {
367                 mem_op().unpin(nvhost_get_host(dev)->memmgr, m->mem_r,
368                         m->pa);
369                 m->pa = NULL;
370         }
371         if (m->mem_r) {
372                 mem_op().put(nvhost_get_host(dev)->memmgr, m->mem_r);
373                 m->mem_r = NULL;
374         }
375 }
376
377 void nvhost_msenc_finalize_poweron(struct platform_device *dev)
378 {
379         msenc_boot(dev);
380 }
381
382 static int __devinit msenc_probe(struct platform_device *dev)
383 {
384         int err = 0;
385         struct nvhost_device_data *pdata =
386                 (struct nvhost_device_data *)dev->dev.platform_data;
387
388         pdata->pdev = dev;
389         pdata->init = nvhost_msenc_init;
390         pdata->deinit = nvhost_msenc_deinit;
391         pdata->finalize_poweron = nvhost_msenc_finalize_poweron;
392
393         platform_set_drvdata(dev, pdata);
394
395         err = nvhost_client_device_get_resources(dev);
396         if (err)
397                 return err;
398
399         err = nvhost_client_device_init(dev);
400         if (err)
401                 return err;
402
403         pm_runtime_use_autosuspend(&dev->dev);
404         pm_runtime_set_autosuspend_delay(&dev->dev, 100);
405         pm_runtime_enable(&dev->dev);
406
407         return 0;
408 }
409
410 static int __exit msenc_remove(struct platform_device *dev)
411 {
412         /* Add clean-up */
413         return 0;
414 }
415
416 #ifdef CONFIG_PM
417 static int msenc_suspend(struct platform_device *dev, pm_message_t state)
418 {
419         return nvhost_client_device_suspend(dev);
420 }
421
422 static int msenc_resume(struct platform_device *dev)
423 {
424         dev_info(&dev->dev, "resuming\n");
425         return 0;
426 }
427 #endif
428
429 static struct platform_driver msenc_driver = {
430         .probe = msenc_probe,
431         .remove = __exit_p(msenc_remove),
432 #ifdef CONFIG_PM
433         .suspend = msenc_suspend,
434         .resume = msenc_resume,
435 #endif
436         .driver = {
437                 .owner = THIS_MODULE,
438                 .name = "msenc",
439         }
440 };
441
442 static int __init msenc_init(void)
443 {
444         return platform_driver_register(&msenc_driver);
445 }
446
447 static void __exit msenc_exit(void)
448 {
449         platform_driver_unregister(&msenc_driver);
450 }
451
452 module_init(msenc_init);
453 module_exit(msenc_exit);