video: tegra: host: handle !PM_RUNTIME
[linux-3.10.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
33 #include <mach/pm_domains.h>
34
35 #include "dev.h"
36 #include "msenc.h"
37 #include "hw_msenc.h"
38 #include "bus_client.h"
39 #include "nvhost_acm.h"
40 #include "nvhost_scale.h"
41 #include "chip_support.h"
42 #include "nvhost_memmgr.h"
43 #include "t114/t114.h"
44 #include "t148/t148.h"
45 #include "t124/t124.h"
46
47 #define MSENC_IDLE_TIMEOUT_DEFAULT      10000   /* 10 milliseconds */
48 #define MSENC_IDLE_CHECK_PERIOD         10      /* 10 usec */
49
50 #define get_msenc(ndev) ((struct msenc *)(ndev)->dev.platform_data)
51 #define set_msenc(ndev, f) ((ndev)->dev.platform_data = f)
52
53 /* caller is responsible for freeing */
54 static char *msenc_get_fw_name(struct platform_device *dev)
55 {
56         char *fw_name;
57         u8 maj, min;
58         struct nvhost_device_data *pdata = platform_get_drvdata(dev);
59
60         /* note size here is a little over...*/
61         fw_name = kzalloc(32, GFP_KERNEL);
62         if (!fw_name)
63                 return NULL;
64
65         decode_msenc_ver(pdata->version, &maj, &min);
66         switch (maj) {
67         case 2:
68                 /* there are no minor versions so far for maj==2 */
69                 sprintf(fw_name, "nvhost_msenc02.fw");
70                 break;
71         case 3:
72                 sprintf(fw_name, "nvhost_msenc03%d.fw", min);
73                 break;
74         default:
75                 kfree(fw_name);
76                 return NULL;
77         }
78
79         dev_info(&dev->dev, "fw name:%s\n", fw_name);
80
81         return fw_name;
82 }
83
84 static int msenc_dma_wait_idle(struct platform_device *dev, u32 *timeout)
85 {
86         if (!*timeout)
87                 *timeout = MSENC_IDLE_TIMEOUT_DEFAULT;
88
89         do {
90                 u32 check = min_t(u32, MSENC_IDLE_CHECK_PERIOD, *timeout);
91                 u32 dmatrfcmd = nvhost_device_readl(dev, msenc_dmatrfcmd_r());
92                 u32 idle_v = msenc_dmatrfcmd_idle_v(dmatrfcmd);
93
94                 if (msenc_dmatrfcmd_idle_true_v() == idle_v)
95                         return 0;
96
97                 udelay(MSENC_IDLE_CHECK_PERIOD);
98                 *timeout -= check;
99         } while (*timeout);
100
101         dev_err(&dev->dev, "dma idle timeout");
102
103         return -1;
104 }
105
106 static int msenc_dma_pa_to_internal_256b(struct platform_device *dev,
107                 u32 offset, u32 internal_offset, bool imem)
108 {
109         u32 cmd = msenc_dmatrfcmd_size_256b_f();
110         u32 pa_offset =  msenc_dmatrffboffs_offs_f(offset);
111         u32 i_offset = msenc_dmatrfmoffs_offs_f(internal_offset);
112         u32 timeout = 0; /* default*/
113
114         if (imem)
115                 cmd |= msenc_dmatrfcmd_imem_true_f();
116
117         nvhost_device_writel(dev, msenc_dmatrfmoffs_r(), i_offset);
118         nvhost_device_writel(dev, msenc_dmatrffboffs_r(), pa_offset);
119         nvhost_device_writel(dev, msenc_dmatrfcmd_r(), cmd);
120
121         return msenc_dma_wait_idle(dev, &timeout);
122
123 }
124
125 static int msenc_wait_idle(struct platform_device *dev, u32 *timeout)
126 {
127         if (!*timeout)
128                 *timeout = MSENC_IDLE_TIMEOUT_DEFAULT;
129
130         do {
131                 u32 check = min_t(u32, MSENC_IDLE_CHECK_PERIOD, *timeout);
132                 u32 w = nvhost_device_readl(dev, msenc_idlestate_r());
133
134                 if (!w)
135                         return 0;
136                 udelay(MSENC_IDLE_CHECK_PERIOD);
137                 *timeout -= check;
138         } while (*timeout);
139
140         return -1;
141 }
142
143 int msenc_boot(struct platform_device *dev)
144 {
145         u32 timeout;
146         u32 offset;
147         int err = 0;
148         struct msenc *m = get_msenc(dev);
149
150         /* check if firmware is loaded or not */
151         if (!m || !m->valid)
152                 return -ENOMEDIUM;
153
154         nvhost_device_writel(dev, msenc_dmactl_r(), 0);
155         nvhost_device_writel(dev, msenc_dmatrfbase_r(),
156                 (sg_dma_address(m->pa->sgl) + m->os.bin_data_offset) >> 8);
157
158         for (offset = 0; offset < m->os.data_size; offset += 256)
159                 msenc_dma_pa_to_internal_256b(dev,
160                                            m->os.data_offset + offset,
161                                            offset, false);
162
163         msenc_dma_pa_to_internal_256b(dev, m->os.code_offset, 0, true);
164
165         /* setup msenc interrupts and enable interface */
166         nvhost_device_writel(dev, msenc_irqmset_r(),
167                         (msenc_irqmset_ext_f(0xff) |
168                                 msenc_irqmset_swgen1_set_f() |
169                                 msenc_irqmset_swgen0_set_f() |
170                                 msenc_irqmset_exterr_set_f() |
171                                 msenc_irqmset_halt_set_f()   |
172                                 msenc_irqmset_wdtmr_set_f()));
173         nvhost_device_writel(dev, msenc_irqdest_r(),
174                         (msenc_irqdest_host_ext_f(0xff) |
175                                 msenc_irqdest_host_swgen1_host_f() |
176                                 msenc_irqdest_host_swgen0_host_f() |
177                                 msenc_irqdest_host_exterr_host_f() |
178                                 msenc_irqdest_host_halt_host_f()));
179         nvhost_device_writel(dev, msenc_itfen_r(),
180                         (msenc_itfen_mthden_enable_f() |
181                                 msenc_itfen_ctxen_enable_f()));
182
183         /* boot msenc */
184         nvhost_device_writel(dev, msenc_bootvec_r(), msenc_bootvec_vec_f(0));
185         nvhost_device_writel(dev, msenc_cpuctl_r(),
186                         msenc_cpuctl_startcpu_true_f());
187
188         timeout = 0; /* default */
189
190         err = msenc_wait_idle(dev, &timeout);
191         if (err != 0) {
192                 dev_err(&dev->dev, "boot failed due to timeout");
193                 return err;
194         }
195
196         return 0;
197 }
198
199 static int msenc_setup_ucode_image(struct platform_device *dev,
200                 u32 *ucode_ptr,
201                 const struct firmware *ucode_fw)
202 {
203         struct msenc *m = get_msenc(dev);
204         /* image data is little endian. */
205         struct msenc_ucode_v1 ucode;
206         int w;
207
208         /* copy the whole thing taking into account endianness */
209         for (w = 0; w < ucode_fw->size / sizeof(u32); w++)
210                 ucode_ptr[w] = le32_to_cpu(((u32 *)ucode_fw->data)[w]);
211
212         ucode.bin_header = (struct msenc_ucode_bin_header_v1 *)ucode_ptr;
213         /* endian problems would show up right here */
214         if (ucode.bin_header->bin_magic != 0x10de) {
215                 dev_err(&dev->dev,
216                            "failed to get firmware magic");
217                 return -EINVAL;
218         }
219         if (ucode.bin_header->bin_ver != 1) {
220                 dev_err(&dev->dev,
221                            "unsupported firmware version");
222                 return -ENOENT;
223         }
224         /* shouldn't be bigger than what firmware thinks */
225         if (ucode.bin_header->bin_size > ucode_fw->size) {
226                 dev_err(&dev->dev,
227                            "ucode image size inconsistency");
228                 return -EINVAL;
229         }
230
231         dev_dbg(&dev->dev,
232                 "ucode bin header: magic:0x%x ver:%d size:%d",
233                 ucode.bin_header->bin_magic,
234                 ucode.bin_header->bin_ver,
235                 ucode.bin_header->bin_size);
236         dev_dbg(&dev->dev,
237                 "ucode bin header: os bin (header,data) offset size: 0x%x, 0x%x %d",
238                 ucode.bin_header->os_bin_header_offset,
239                 ucode.bin_header->os_bin_data_offset,
240                 ucode.bin_header->os_bin_size);
241         ucode.os_header = (struct msenc_ucode_os_header_v1 *)
242                 (((void *)ucode_ptr) + ucode.bin_header->os_bin_header_offset);
243
244         dev_dbg(&dev->dev,
245                 "os ucode header: os code (offset,size): 0x%x, 0x%x",
246                 ucode.os_header->os_code_offset,
247                 ucode.os_header->os_code_size);
248         dev_dbg(&dev->dev,
249                 "os ucode header: os data (offset,size): 0x%x, 0x%x",
250                 ucode.os_header->os_data_offset,
251                 ucode.os_header->os_data_size);
252         dev_dbg(&dev->dev,
253                 "os ucode header: num apps: %d",
254                 ucode.os_header->num_apps);
255
256         m->os.size = ucode.bin_header->os_bin_size;
257         m->os.bin_data_offset = ucode.bin_header->os_bin_data_offset;
258         m->os.code_offset = ucode.os_header->os_code_offset;
259         m->os.data_offset = ucode.os_header->os_data_offset;
260         m->os.data_size   = ucode.os_header->os_data_size;
261
262         return 0;
263 }
264
265 int msenc_read_ucode(struct platform_device *dev, const char *fw_name)
266 {
267         struct msenc *m = get_msenc(dev);
268         const struct firmware *ucode_fw;
269         int err;
270
271         ucode_fw  = nvhost_client_request_firmware(dev, fw_name);
272         if (!ucode_fw) {
273                 dev_err(&dev->dev, "failed to get msenc firmware\n");
274                 err = -ENOENT;
275                 return err;
276         }
277
278         /* allocate pages for ucode */
279         m->mem_r = nvhost_memmgr_alloc(nvhost_get_host(dev)->memmgr,
280                                      roundup(ucode_fw->size, PAGE_SIZE),
281                                      PAGE_SIZE, mem_mgr_flag_uncacheable, 0);
282         if (IS_ERR(m->mem_r)) {
283                 dev_err(&dev->dev, "nvmap alloc failed");
284                 err = PTR_ERR(m->mem_r);
285                 goto clean_up;
286         }
287
288         m->pa = nvhost_memmgr_pin(nvhost_get_host(dev)->memmgr, m->mem_r,
289                         &dev->dev);
290         if (IS_ERR(m->pa)) {
291                 dev_err(&dev->dev, "nvmap pin failed for ucode");
292                 err = PTR_ERR(m->pa);
293                 m->pa = NULL;
294                 goto clean_up;
295         }
296
297         m->mapped = nvhost_memmgr_mmap(m->mem_r);
298         if (IS_ERR_OR_NULL(m->mapped)) {
299                 dev_err(&dev->dev, "nvmap mmap failed");
300                 err = -ENOMEM;
301                 goto clean_up;
302         }
303
304         err = msenc_setup_ucode_image(dev, (u32 *)m->mapped, ucode_fw);
305         if (err) {
306                 dev_err(&dev->dev, "failed to parse firmware image\n");
307                 return err;
308         }
309
310         m->valid = true;
311
312         release_firmware(ucode_fw);
313
314         return 0;
315
316 clean_up:
317         if (m->mapped) {
318                 nvhost_memmgr_munmap(m->mem_r, (u32 *)m->mapped);
319                 m->mapped = NULL;
320         }
321         if (m->pa) {
322                 nvhost_memmgr_unpin(nvhost_get_host(dev)->memmgr, m->mem_r,
323                                 &dev->dev, m->pa);
324                 m->pa = NULL;
325         }
326         if (m->mem_r) {
327                 nvhost_memmgr_put(nvhost_get_host(dev)->memmgr, m->mem_r);
328                 m->mem_r = NULL;
329         }
330         release_firmware(ucode_fw);
331         return err;
332 }
333
334 int nvhost_msenc_init(struct platform_device *dev)
335 {
336         struct nvhost_device_data *pdata = platform_get_drvdata(dev);
337         int err = 0;
338         struct msenc *m;
339         char *fw_name;
340
341         fw_name = msenc_get_fw_name(dev);
342         if (!fw_name) {
343                 dev_err(&dev->dev, "couldn't determine firmware name");
344                 return -EINVAL;
345         }
346
347         m = kzalloc(sizeof(struct msenc), GFP_KERNEL);
348         if (!m) {
349                 dev_err(&dev->dev, "couldn't alloc ucode");
350                 kfree(fw_name);
351                 return -ENOMEM;
352         }
353         set_msenc(dev, m);
354
355         err = msenc_read_ucode(dev, fw_name);
356         kfree(fw_name);
357         fw_name = 0;
358
359         if (err || !m->valid) {
360                 dev_err(&dev->dev, "ucode not valid");
361                 goto clean_up;
362         }
363
364         nvhost_module_busy(dev);
365         msenc_boot(dev);
366         nvhost_module_idle(dev);
367
368         if (pdata->scaling_init)
369                 nvhost_scale_hw_init(dev);
370
371         return 0;
372
373 clean_up:
374         dev_err(&dev->dev, "failed");
375         return err;
376 }
377
378 void nvhost_msenc_deinit(struct platform_device *dev)
379 {
380         struct msenc *m = get_msenc(dev);
381         struct nvhost_device_data *pdata = platform_get_drvdata(dev);
382
383         if (pdata->scaling_init)
384                 nvhost_scale_hw_deinit(dev);
385
386         if (!m)
387                 return;
388
389         /* unpin, free ucode memory */
390         if (m->mapped) {
391                 nvhost_memmgr_munmap(m->mem_r, m->mapped);
392                 m->mapped = NULL;
393         }
394         if (m->pa) {
395                 nvhost_memmgr_unpin(nvhost_get_host(dev)->memmgr, m->mem_r,
396                         &dev->dev, m->pa);
397                 m->pa = NULL;
398         }
399         if (m->mem_r) {
400                 nvhost_memmgr_put(nvhost_get_host(dev)->memmgr, m->mem_r);
401                 m->mem_r = NULL;
402         }
403         kfree(m);
404         set_msenc(dev, NULL);
405         m->valid = false;
406 }
407
408 int nvhost_msenc_finalize_poweron(struct platform_device *dev)
409 {
410         return msenc_boot(dev);
411 }
412
413 static struct of_device_id tegra_msenc_of_match[] = {
414 #ifdef TEGRA_11X_OR_HIGHER_CONFIG
415         { .compatible = "nvidia,tegra114-msenc",
416                 .data = (struct nvhost_device_data *)&t11_msenc_info },
417 #endif
418 #ifdef TEGRA_14X_OR_HIGHER_CONFIG
419         { .compatible = "nvidia,tegra148-msenc",
420                 .data = (struct nvhost_device_data *)&t14_msenc_info },
421 #endif
422 #ifdef TEGRA_12X_OR_HIGHER_CONFIG
423         { .compatible = "nvidia,tegra124-msenc",
424                 .data = (struct nvhost_device_data *)&t14_msenc_info },
425 #endif
426         { },
427 };
428
429 #ifdef CONFIG_PM_GENERIC_DOMAINS
430 static int msenc_unpowergate(struct generic_pm_domain *domain)
431 {
432         struct nvhost_device_data *pdata;
433
434         pdata = container_of(domain, struct nvhost_device_data, pd);
435         return nvhost_module_power_on(pdata->pdev);
436 }
437
438 static int msenc_powergate(struct generic_pm_domain *domain)
439 {
440         struct nvhost_device_data *pdata;
441
442         pdata = container_of(domain, struct nvhost_device_data, pd);
443         return nvhost_module_power_off(pdata->pdev);
444 }
445 #endif
446
447 static int msenc_probe(struct platform_device *dev)
448 {
449         int err = 0;
450         struct nvhost_device_data *pdata = NULL;
451
452         if (dev->dev.of_node) {
453                 const struct of_device_id *match;
454
455                 match = of_match_device(tegra_msenc_of_match, &dev->dev);
456                 if (match)
457                         pdata = (struct nvhost_device_data *)match->data;
458         } else
459                 pdata = (struct nvhost_device_data *)dev->dev.platform_data;
460
461         WARN_ON(!pdata);
462         if (!pdata) {
463                 dev_info(&dev->dev, "no platform data\n");
464                 return -ENODATA;
465         }
466
467         pdata->pdev = dev;
468         pdata->init = nvhost_msenc_init;
469         pdata->deinit = nvhost_msenc_deinit;
470         pdata->finalize_poweron = nvhost_msenc_finalize_poweron;
471
472         mutex_init(&pdata->lock);
473
474         platform_set_drvdata(dev, pdata);
475         dev->dev.platform_data = NULL;
476
477         /* get the module clocks to sane state */
478         nvhost_module_init(dev);
479
480 #ifdef CONFIG_PM_GENERIC_DOMAINS
481         pdata->pd.name = "msenc";
482         pdata->pd.power_off = msenc_powergate;
483         pdata->pd.power_on = msenc_unpowergate;
484         pdata->pd.dev_ops.start = nvhost_module_enable_clk;
485         pdata->pd.dev_ops.stop = nvhost_module_disable_clk;
486
487         /* add module power domain and also add its domain
488          * as sub-domain of MC domain */
489         err = nvhost_module_add_domain(&pdata->pd, dev);
490
491         /* overwrite save/restore fptrs set by pm_genpd_init */
492         pdata->pd.domain.ops.suspend = nvhost_client_device_suspend;
493         pdata->pd.domain.ops.resume = nvhost_client_device_resume;
494         pdata->pd.dev_ops.restore_state = nvhost_module_finalize_poweron;
495 #endif
496
497         /* enable runtime pm. this is needed now since we need to call
498          * _get_sync/_put during boot-up to ensure MC domain is ON */
499 #ifdef CONFIG_PM_RUNTIME
500         if (pdata->clockgate_delay) {
501                 pm_runtime_set_autosuspend_delay(&dev->dev,
502                         pdata->clockgate_delay);
503                 pm_runtime_use_autosuspend(&dev->dev);
504         }
505         pm_runtime_enable(&dev->dev);
506         pm_runtime_get_sync(&dev->dev);
507 #else
508         nvhost_module_enable_clk(&dev->dev);
509 #endif
510
511         err = nvhost_client_device_get_resources(dev);
512         if (err)
513                 return err;
514
515         err = nvhost_client_device_init(dev);
516
517 #ifdef CONFIG_PM_RUNTIME
518         if (pdata->clockgate_delay)
519                 pm_runtime_put_sync_autosuspend(&dev->dev);
520         else
521                 pm_runtime_put(&dev->dev);
522         if (err)
523                 return err;
524 #endif
525
526         return 0;
527 }
528
529 static int __exit msenc_remove(struct platform_device *dev)
530 {
531 #ifdef CONFIG_PM_RUNTIME
532         pm_runtime_put(&dev->dev);
533         pm_runtime_disable(&dev->dev);
534 #else
535         nvhost_module_disable_clk(&dev->dev);
536 #endif
537         return 0;
538 }
539
540 static struct platform_driver msenc_driver = {
541         .probe = msenc_probe,
542         .remove = __exit_p(msenc_remove),
543         .driver = {
544                 .owner = THIS_MODULE,
545                 .name = "msenc",
546 #ifdef CONFIG_OF
547                 .of_match_table = tegra_msenc_of_match,
548 #endif
549 #ifdef CONFIG_PM
550                 .pm = &nvhost_module_pm_ops,
551 #endif
552         }
553 };
554
555 static int __init msenc_init(void)
556 {
557         return platform_driver_register(&msenc_driver);
558 }
559
560 static void __exit msenc_exit(void)
561 {
562         platform_driver_unregister(&msenc_driver);
563 }
564
565 module_init(msenc_init);
566 module_exit(msenc_exit);