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