diff --git a/drivers/video/tegra/host/nvdla/dla_os_interface.h b/drivers/video/tegra/host/nvdla/dla_os_interface.h index 5b6c6268..4ba7e769 100644 --- a/drivers/video/tegra/host/nvdla/dla_os_interface.h +++ b/drivers/video/tegra/host/nvdla/dla_os_interface.h @@ -157,6 +157,7 @@ struct dla_action_semaphore { enum dla_regions_e { DLA_REGION_PRINTF = 1, DLA_REGION_GOS = 2, + DLA_REGION_TRACE = 3, }; /** diff --git a/drivers/video/tegra/host/nvdla/nvdla.c b/drivers/video/tegra/host/nvdla/nvdla.c index 92edbc84..fc6a3ed3 100644 --- a/drivers/video/tegra/host/nvdla/nvdla.c +++ b/drivers/video/tegra/host/nvdla/nvdla.c @@ -143,13 +143,12 @@ int nvdla_send_cmd(struct platform_device *pdev, return ret; } -static int nvdla_alloc_dump_region(struct platform_device *pdev) +static int nvdla_alloc_trace_region(struct platform_device *pdev) { int err = 0; struct flcn *m; - dma_addr_t region_pa; - struct dla_region_printf *region; - u32 timeout = FLCN_IDLE_TIMEOUT_DEFAULT * 5; + dma_addr_t tregion_pa; + struct dla_region_printf *trace_region = NULL; struct nvhost_device_data *pdata = platform_get_drvdata(pdev); if (!pdata->flcn_isr) @@ -158,6 +157,80 @@ static int nvdla_alloc_dump_region(struct platform_device *pdev) nvdla_dbg_fn(pdev, ""); m = get_flcn(pdev); + if (!m) + return -ENXIO; + + /* Trace buffer allocation must be done at once only. */ + if (!m->trace_dump_va) { + /* allocate trace region */ + m->trace_dump_va = dma_alloc_attrs(&pdev->dev, + TRACE_BUFFER_SIZE, &m->trace_dump_pa, + GFP_KERNEL, &attrs); + + if (!m->trace_dump_va) { + nvdla_dbg_err(pdev, + "dma trace memory allocation failed"); + return -ENOMEM; + } + } + + /* allocate memory for trace command */ + trace_region = (struct dla_region_printf *) + dma_alloc_attrs(&pdev->dev, + sizeof(struct dla_region_printf), + &tregion_pa, GFP_KERNEL, &attrs); + if (!trace_region) { + nvdla_dbg_err(pdev, + "dma allocation failed for trace command."); + err = -ENOMEM; + goto alloc_trace_cmd_failed; + } + + trace_region->region = DLA_REGION_TRACE; + trace_region->address = m->trace_dump_pa; + trace_region->size = TRACE_BUFFER_SIZE; + + err = nvdla_send_cmd(pdev, DLA_CMD_SET_REGIONS, + ALIGNED_DMA(tregion_pa), true); + + /* free memory allocated for trace command */ + dma_free_attrs(&pdev->dev, sizeof(struct dla_region_printf), + trace_region, tregion_pa, &attrs); + + if (err != 0) { + nvdla_dbg_err(pdev, "failed to send trace command"); + goto trace_send_cmd_failed; + } + + return err; + +trace_send_cmd_failed: +alloc_trace_cmd_failed: + dma_free_attrs(&pdev->dev, TRACE_BUFFER_SIZE, + m->trace_dump_va, m->trace_dump_pa, &attrs); + m->trace_dump_va = NULL; + m->trace_dump_pa = 0; + + return err; +} + +static int nvdla_alloc_dump_region(struct platform_device *pdev) +{ + int err = 0; + struct flcn *m; + dma_addr_t region_pa; + struct dla_region_printf *region; + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + + if (!pdata->flcn_isr) + return 0; + + nvdla_dbg_fn(pdev, ""); + + m = get_flcn(pdev); + if (!m) + return -ENXIO; + /* allocate dump region */ m->debug_dump_va = dma_alloc_attrs(&pdev->dev, DEBUG_BUFFER_SIZE, &m->debug_dump_pa, @@ -182,25 +255,27 @@ static int nvdla_alloc_dump_region(struct platform_device *pdev) region->size = DEBUG_BUFFER_SIZE; /* pass dump region to falcon */ - nvdla_send_cmd(pdev, DLA_CMD_SET_REGIONS, - ALIGNED_DMA(region_pa), false); + err = nvdla_send_cmd(pdev, DLA_CMD_SET_REGIONS, + ALIGNED_DMA(region_pa), true); - /* wait for falcon to idle */ - err = flcn_wait_idle(pdev, &timeout); - if (err != 0) - dev_err(&pdev->dev, "failed for wait for idle in timeout"); - /* free memory allocated for command */ + /* free memory allocated for debug print command */ dma_free_attrs(&pdev->dev, sizeof(struct dla_region_printf), - region, region_pa, - &attrs); + region, region_pa, &attrs); + + if (err != 0) { + nvdla_dbg_err(pdev, "failed to send printf command"); + goto region_send_cmd_failed; + } return 0; +region_send_cmd_failed: set_region_failed: dma_free_attrs(&pdev->dev, DEBUG_BUFFER_SIZE, - m->debug_dump_va, m->debug_dump_pa, - &attrs); + m->debug_dump_va, m->debug_dump_pa, &attrs); + m->debug_dump_va = NULL; + m->debug_dump_pa = 0; return err; } @@ -216,7 +291,7 @@ static void nvdla_free_dump_region(struct platform_device *pdev) return; m = get_flcn(pdev); - if (m->debug_dump_pa) { + if (m && m->debug_dump_pa) { dma_free_attrs(&pdev->dev, DEBUG_BUFFER_SIZE, m->debug_dump_va, m->debug_dump_pa, &attrs); @@ -262,6 +337,10 @@ int nvhost_nvdla_finalize_poweron(struct platform_device *pdev) if (ret) nvhost_nvdla_prepare_poweroff(pdev); + ret = nvdla_alloc_trace_region(pdev); + if (ret) + nvhost_nvdla_prepare_poweroff(pdev); + return ret; } @@ -402,10 +481,23 @@ static int __exit nvdla_remove(struct platform_device *pdev) { struct nvhost_device_data *pdata = platform_get_drvdata(pdev); struct nvdla_device *nvdla_dev = pdata->private_data; + struct flcn *m; nvhost_queue_deinit(nvdla_dev->pool); nvhost_client_device_release(pdev); + m = get_flcn(pdev); + if (!m) + return -ENXIO; + + if (m->trace_dump_pa) { + dma_free_attrs(&pdev->dev, TRACE_BUFFER_SIZE, + m->trace_dump_va, m->trace_dump_pa, + &attrs); + m->trace_dump_va = NULL; + m->trace_dump_pa = 0; + } + nvdla_dbg_fn(pdev, ""); return 0; diff --git a/drivers/video/tegra/host/nvdla/nvdla.h b/drivers/video/tegra/host/nvdla/nvdla.h index 8b56b5c2..41b60a47 100644 --- a/drivers/video/tegra/host/nvdla/nvdla.h +++ b/drivers/video/tegra/host/nvdla/nvdla.h @@ -54,6 +54,11 @@ */ #define MAX_NVDLA_QUEUE_COUNT 16 +/** + * Trace Buffer Size + */ +#define TRACE_BUFFER_SIZE 0x100000 + /** * data structure to keep per DLA engine device data * @@ -61,7 +66,6 @@ * @pool pointer to queue table * @dbg_mask debug mask for print level * @en_trace flag to enable tracing - * */ struct nvdla_device { struct platform_device *pdev; diff --git a/drivers/video/tegra/host/nvdla/nvdla_debug.c b/drivers/video/tegra/host/nvdla/nvdla_debug.c index f0b16cf5..1db8ada5 100644 --- a/drivers/video/tegra/host/nvdla/nvdla_debug.c +++ b/drivers/video/tegra/host/nvdla/nvdla_debug.c @@ -19,8 +19,21 @@ #include #include #include +#include "host1x/host1x.h" +#include "flcn/flcn.h" +#include "flcn/hw_flcn.h" +#include "dla_os_interface.h" +#include #include "nvdla/nvdla.h" +#include "nvdla_debug.h" + +/* + * Header in ring buffer consist (start, end) two uint32_t values. + * Trace data content starts from the offset below. + */ +#define TRACE_DATA_OFFSET (2 * sizeof(uint32_t)) + static int nvdla_fw_ver_show(struct seq_file *s, void *unused) { struct nvdla_device *nvdla_dev; @@ -57,8 +70,83 @@ static const struct file_operations nvdla_fw_ver_fops = { .release = single_release, }; +static int debug_dla_dump_show(struct seq_file *s, void *data) +{ + char *bufptr; + struct flcn *m; + struct nvdla_device *nvdla_dev; + struct platform_device *pdev; + uint32_t i = 0, cindex = 0; + uint32_t offset = TRACE_DATA_OFFSET; + uint32_t start, end, datasize; + + nvdla_dev = (struct nvdla_device *)s->private; + pdev = nvdla_dev->pdev; + m = get_flcn(pdev); + + if (!m) + return 0; + + if (m->trace_dump_va) { + bufptr = (char *)m->trace_dump_va; + + if (!strcmp(bufptr, "")) + return 0; + + memcpy(&start, bufptr, sizeof(uint32_t)); + memcpy(&end, ((char *)bufptr + sizeof(uint32_t)), + sizeof(uint32_t)); + + i = start; + + if (start == (end + 1)) + datasize = (uint32_t)TRACE_BUFFER_SIZE - offset; + else + datasize = end - start; + + while (cindex < datasize) { + seq_printf(s, "%c", bufptr[i]); + i++; + i = ((i - offset) % (TRACE_BUFFER_SIZE - offset)) + + offset; + cindex++; + + if ((bufptr[i] == '\n') && (cindex < datasize)) { + seq_printf(s, "%c", bufptr[i]); + + /* skip extra new line chars */ + while ((bufptr[i] == '\n') && + (cindex < datasize)) { + i++; + i = ((i - offset) % + (TRACE_BUFFER_SIZE - offset)) + + offset; + cindex++; + } + } + } + + seq_printf(s, "%c", '\n'); + } + + return 0; +} + +static int debug_dla_trace_open(struct inode *inode, struct file *file) +{ + return single_open(file, debug_dla_dump_show, inode->i_private); +} + +static const struct file_operations debug_dla_trace_fops = { + .open = debug_dla_trace_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + void nvdla_debug_init(struct platform_device *pdev) { + struct dentry *ret; struct nvhost_device_data *pdata = platform_get_drvdata(pdev); struct nvdla_device *nvdla_dev = pdata->private_data; struct dentry *de = pdata->debugfs; @@ -66,10 +154,14 @@ void nvdla_debug_init(struct platform_device *pdev) if (!de) return; - debugfs_create_u32("debug_mask", S_IRUGO|S_IWUSR, de, + debugfs_create_u32("debug_mask", S_IRUGO | S_IWUSR, de, &nvdla_dev->dbg_mask); - debugfs_create_u32("en_trace", S_IRUGO|S_IWUSR, de, + debugfs_create_u32("en_trace", S_IRUGO | S_IWUSR, de, &nvdla_dev->en_trace); debugfs_create_file("fw_version", S_IRUGO, de, nvdla_dev, &nvdla_fw_ver_fops); + ret = debugfs_create_file("fw_trace", S_IRUGO, + de, nvdla_dev, &debug_dla_trace_fops); + if (!ret) + nvdla_dbg_err(pdev, "Failed to create trace debug file!\n"); }